956k Swiss court decisions: full-text search, citation graph, statute lookup (DE/FR/IT)
Copy the AI prompt to install this server into Claude Code, Cursor, or another agent β or use 1-click editor setup below.
We haven't yet run this listing's install command through our automated sandbox check. This isn't a red flag β we're steadily working through the catalog.
π‘ Paste into ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows)
Inspect callable tools, capabilities, and parameters exposed to AI agents by Swiss Case Law.
search_decisionsFull-text search with filters (court, canton, language, date range, chamber)
get_decisionFetch a single decision by docket number or ID. Includes citation graph counts (cited by / cites).
list_courtsList all courts with decision counts
get_statisticsAggregate stats by court, canton, or year
find_citationsShow what a decision cites and what cites it, with confidence scores
find_appeal_chainTrace the appeal chain (Instanzenzug) β prior and subsequent instances
Open Swiss case law and legislation for legal research and AI.
1M+ decisions Β· all 26 cantons Β· 1875βtoday Β· citation graph Β· MCP + REST + Parquet Β· CC0 data Β· MIT code
Try the search Β· Connect an AI client Β· Download the dataset Β· API documentation
No sign-up, no API key, read-only. 42 tools; setup guides for individual clients are at mcp.opencaselaw.ch.
Question. When is a dismissal in retaliation for an employee asserting a claim abusive under Swiss law?
One call β search_decisions("missbrΓ€uchliche KΓΌndigung RachekΓΌndigung")
returns 129 decisions, the leading one first:
BGE 136 III 513 (7 October 2010)
Art. 336 OR; missbrΓ€uchliche KΓΌndigung. Der Arbeitnehmer ist vor einer RachekΓΌndigung nur geschΓΌtzt (Art. 336 Abs. 1 lit. d OR), sofern er nach Treu und Glauben annehmen kann, dass die von ihm geltend gemachten AnsprΓΌche berechtigt sind. Es ist nicht erforderlich, dass sie tatsΓ€chlich begrΓΌndet sind.
The Regeste is returned verbatim from the stored official head-note, and the citation string comes from a stored field β tools never construct citations. That contract is what makes the answer checkable rather than plausible.
Reproduce it:
| Resource | Coverage |
|---|---|
| Court decisions | 1,050,000+ records, 118 court and source collections, 1875βtoday |
| Languages | German, French, Italian |
| Legislation | 5,525 federal laws, 15,600 cantonal laws (all 26 cantons) |
| Citation graph | ~10M resolved decision-to-decision edges, 12.4M statute references |
| Interpretive layers | Federal Council dispatches, commentaries, open-access scholarship, federal administrative practice |
| Access | Web, MCP, REST, daily CC0 Parquet |
| Licensing | CC0 data, MIT code (ECtHR texts Β© ECHR-CEDH, see the data card) |
Counts are generated from one source of truth,
docs/canonical_numbers.md, with the
verification method recorded alongside each figure.
Limitations, stated plainly. Coverage is not complete where portals restrict access; those sources are documented rather than glossed. The citation graph reports resolution coverage (93.8 %), which is not a precision estimate β a human semantic audit is in preparation. Some cantonal decisions may be published unredacted by the court; we inherit upstream pseudonymisation and do not re-pseudonymise. See governance and removal policy.
Data-quality reports are the most valuable contribution β every one so far has been reproduced, fixed and pinned with a regression test. See CONTRIBUTING.md.
The remainder of this page is reference detail.
Swiss legal research today is fragmented across paywalls, inaccessible to language models, and prohibitively expensive for the people who need it most β law students, independent researchers, and anyone outside the major firms. Commercial databases (Weblaw, Swisslex, Legalis) charge hundreds of francs per month and still don't expose a clean API. LLMs hallucinate statute text because they have no authoritative source. Small cantons publish decisions in PDF archives nobody indexes.
OpenCaseLaw fixes this. Every published Swiss court decision, every federal and cantonal law, the resolved citation graph between them, and 44 MCP tools (42 remote in public mode + 2 local-only) that let any modern LLM act as a Swiss legal research assistant β all free, all open, all refreshed automatically.
Case law β 1,050,000+ decision records (~909,000 unique decisions; cross-identifier duplicates retained and linked, never deleted) from 1875 to today across 118 courts, full text + structured metadata, covering:
rm for RomanshLegislation β every Swiss law, federal and cantonal, locally mirrored with article-level indexing:
Citation graph β the only public large-scale citation graph of the Swiss legal system:
find_leading_cases, find_citations, find_appeal_chain, analyze_legal_trend (top: BGE 125 V 351 with 85,108 incoming citations)44 MCP tools (42 remote in public mode + 2 local-only) β specialised research tools that run in your LLM of choice:
search_botschaft (topical FTS5 across the verbatim corpus), get_article_purpose (verbatim Botschaft text for a specific article), and get_article_history (chronological timeline composing statute + Botschaft + leading cases + commentary); per-article digests (legislative intent, key arguments, design choices, rejected alternatives) for BV and BGFA; parliamentary debate transcripts for the BV. Full verbatim ingest to ~25K Botschaften via Fedlex SPARQL discovery is scaling.get_decision_structure (Sachverhalt + ErwΓ€gungen + Dispositiv + Regeste split), get_erwaegung (verbatim Schweizer-citation Einheit, e.g. get_erwaegung("BGE 140 III 86", "2.3")), get_regeste (official BGer/BVGer/BStGer head-note)Multiple access paths β same data, two distinct audiences:
For LLM users, researchers and developers β full 24-tool surface:
mcp.opencaselaw.ch (SSE + Streamable HTTP) β 30-second setup in any MCP client (Claude, ChatGPT, Cursor, Gemini, Windsurf)For legal practitioners drafting documents β curated practitioner surface:
Performance you can defend in a paper:
| Metric | Value |
|---|---|
| Decision search quality | MRR@10 = 0.647 (online) / 0.470 (offline reproducible) Β· +102 % over baseline |
| Article lookup latency | < 1 ms (local FTS5) |
| BGer publication β searchable | ~15 min (was 24 h pre-poller) |
| Daily full-text rebuild | ~5 h, zero downtime (atomic swap) |
| Citation-to-decision resolution | 9.65 M edges resolved (10 M extracted) |
A structured, searchable archive of Swiss court decisions β from the Federal Supreme Court (BGer) down to cantonal courts in all 26 cantons. Every decision includes the full decision text, docket number, date, language, legal area, judges, cited decisions, and 20+ additional metadata fields.
The dataset is built by direct scraping of official court websites and cantonal court portals. New decisions are scraped, deduplicated, and published every night.
There are eight ways to use it, depending on what you need:
| Method | For whom | What you get |
|---|---|---|
| Search with AI | Everyone | Natural-language search in Claude, ChatGPT, Cursor, or Gemini β instant access, no download, full 24-tool surface |
| Citation Analysis | Legal scholars, researchers | Leading cases, citation networks, appeal chains, jurisprudence trends over time |
| Statute Lookup | Legal professionals | Full article text from 5,525 federal laws and 15,600 cantonal laws, federated FTS5, sub-millisecond lookup |
| Legislation Search | Legal professionals | LexFind-backed discovery search with fetch_top_n_texts for single-call natural-language workflows |
| Education tools | Law students, instructors | Structured case briefs, doctrine timelines, real-BGE exam questions with hidden analysis |
| Word Add-in | Legal practitioners writing briefs | Insert formatted Swiss citations into Word Β· click ErwΓ€gung / Β§ to insert with correct sub-reference Β· free Audit (5-rail citation check) Β· Pro (CHF 5/mo, 25 AI calls/day): Verify (citation vs. full text) Β· Strengthen (paragraph x-ray + leading-case suggestions) Β· Find Support (decisions backing a statement) Β· Reflect (literary mirror) β curated ~8-tool subset |
| REST API / Download | Developers, data scientists, NLP researchers | 30-route REST API, bulk Parquet download via HuggingFace (~7 GB) |
| Web UI | Everyone | Live dashboard with corpus stats, daily delta, top movers, multilingual browsing |
Not sure where to start? Connect to the remote MCP server β works with Claude, ChatGPT, and Gemini CLI. Instant access to all 1,050,000+ decisions, citation analysis, statute lookup, legislation search, and education tools, no download needed.
The dataset comes with an MCP server whose exact tool surface is deployment-dependent. Local deployments expose all 44 tools; remote mode omits the 2 local-only update tools (update_database, check_update_status) for 42 remote tools. You ask a question in natural language; the tool runs a full-text search and returns matching decisions with snippets.
| Remote | Local | |
|---|---|---|
| Setup | 30 seconds | 30β60 minutes |
| Disk | None | ~65 GB |
| Tools | 38 (no local update tools) | 40, including update_database and check_update_status |
| Freshness | Nightly (automatic) | Manual |
| Offline | No | Yes |
| Requires | Claude, ChatGPT, or Gemini CLI (see plans below) | Any MCP client |
Start with the remote server. Switch to local only if you need offline access.
Connect directly to the hosted MCP server β no data download, no local database, instant access to 1,050,000+ decisions.
Claude.ai / Claude Desktop (easiest):
https://mcp.opencaselaw.chSame steps in the browser (claude.ai) and the desktop app. No Node.js, no config files, no downloads.
Available on Pro, Max, Team, and Enterprise plans. For the free plan, use Claude Code or the manual JSON config.
Claude Code:
Add to claude_desktop_config.json (Node.js required):
Restart Claude Desktop after saving.
ChatGPT:
Swiss Caselaw, paste https://mcp.opencaselaw.ch/sse, auth: NoneAvailable on Plus, Pro, Team, Enterprise, and Edu plans. Recommended with GPT-5.3 (GPT-5.4 does not currently support MCP tool invocation).
Gemini CLI:
Add to ~/.gemini/settings.json:
Restart Gemini CLI after saving. No account plan required β Gemini CLI is free.
Google ADK (Agent Development Kit):
Build a Gemini-powered agent with access to all tools:
See the full MCP setup guide for Google Gen AI SDK examples and all other platforms.
See the full MCP setup guide for detailed instructions for all platforms.
The
update_databaseandcheck_update_statustools are only available on the local server β the remote dataset is updated automatically every night.
Run the MCP server locally with your own copy of the database (~65 GB disk). This gives you offline access and full control over the data.
Claude Code is Anthropic's CLI for working with Claude in the terminal.
Step 1. Clone this repository:
Step 2. Create a virtual environment and install the MCP server dependencies:
Step 3. Register the MCP server with Claude Code:
Use the full absolute path to the Python binary inside .venv so that the server always finds its dependencies, regardless of which directory you run Claude Code from.
Step 4. Restart Claude Code and run your first search.
On first use, the server automatically:
~/.swiss-caselaw/parquet/~/.swiss-caselaw/decisions.db (~58 GB)This takes 30β60 minutes depending on your machine and connection. It only happens once β after that, searches run instantly against the local database.
Total disk usage: ~65 GB in ~/.swiss-caselaw/ (macOS/Linux) or %USERPROFILE%\.swiss-caselaw\ (Windows).
See the Claude Desktop setup guide for step-by-step instructions (macOS + Windows).
Quick version β add this to your claude_desktop_config.json:
Config file location: ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows). On Windows, use .venv\\Scripts\\python.exe instead.
Any MCP-compatible client works with the same command + args pattern.
The dataset is updated daily. To get the latest decisions, ask Claude to run the update_database tool, or call it explicitly. This re-downloads the Parquet files from HuggingFace and rebuilds the local database.
The HuggingFace dataset may also publish a full compressed SQLite base snapshot for bootstrap tools under artifacts/sqlite/snapshots/. Its metadata lives in artifacts/manifest.json as snapshot; if that value is null, consumers should fall back to the Parquet rebuild path above.
The snapshot is intended for local MCP/server bootstrapping: download artifacts/manifest.json, fetch snapshot.sqlite_zst.path, verify snapshot.sqlite_zst.sha256, decompress to decisions.db.tmp, run a quick SQLite row/schema check, then atomically move it to decisions.db. Newer artifacts/sqlite/deltas/*.sqlite.zst entries can then be applied by tools that support delta updates.
Manual snapshot bootstrap:
All data stays on your machine. No API calls are made during search β the MCP server queries the local SQLite database directly.
Database structure. decisions.db is a single SQLite file with two tables:
decisions β the main table with one row per decision. Holds 24 columns, including the search-facing fields plus json_data (full record blob) and canonical_key for dedup-aware lookup. Indexed on court, canton, decision_date, language, docket_number, chamber, and decision_type for fast filtered queries.
decisions_fts β an FTS5 virtual table that mirrors 7 text columns from decisions: court, canton, docket_number, language, title, regeste, and full_text. FTS5 builds an inverted index over these columns, enabling sub-second full-text search across 1M+ decisions. The tokenizer is unicode61 remove_diacritics 2, which handles accented characters across German, French, Italian, and Romansh. Insert/update/delete triggers keep the FTS index in sync with the main table automatically.
Why ~58 GB. The full text of 962K+ court decisions averages ~15 KB per decision. The FTS5 inverted index adds overhead for every unique token, its position, and the column it appears in. This is a known trade-off: FTS5 indexes over large text corpora are substantially larger than the source data, but they enable instant ranked search without external infrastructure.
Search pipeline. When you search, the server:
Detects query intent β docket number lookup (6B_1234/2023), explicit FTS syntax (Mietrecht AND KΓΌndigung), or natural language (decisions on tenant eviction).
Runs multiple FTS5 query strategies β For natural-language queries, the server generates several FTS query variants (AND, OR, phrase, field-focused on regeste/title, with multilingual term expansion) and executes them in sequence. Each strategy produces a ranked candidate set. For explicit syntax (AND/OR/NOT, quoted phrases, column filters), the raw query is tried first.
Fuses candidates via RRF β Results from all strategies are merged using Reciprocal Rank Fusion: each candidate's score is the weighted sum of 1/(k + rank) across all strategies that returned it. Decisions found by multiple strategies get a boost.
Reranks with signal scoring β The top candidates are reranked using a composite score that combines:
full_text 1.2, regeste 5.0, title 6.0 β headnotes and titles are weighted heavily over body text)Selects the best passage β For each result, the server scans the full text for the most relevant passage and returns it as a snippet.
Available on both remote and local unless noted.
| Tool | Description |
|---|---|
search_decisions | Full-text search with filters (court, canton, language, date range, chamber) |
get_decision | Fetch a single decision by docket number or ID. Includes citation graph counts (cited by / cites). |
list_courts | List all courts with decision counts |
get_statistics | Aggregate stats by court, canton, or year |
find_citations | Show what a decision cites and what cites it, with confidence scores |
find_appeal_chain | Trace the appeal chain (Instanzenzug) β prior and subsequent instances |
find_leading_cases | Find the most-cited decisions for a topic or statute |
analyze_legal_trend | Year-by-year decision counts for a statute or topic |
draft_mock_decision | Build a research-only mock decision outline from facts, grounded in caselaw + statute references; asks clarification questions before conclusion |
get_case_brief | Structured brief for any case β regeste, key ErwΓ€gungen, cited statutes, and citation authority |
get_doctrine | Statute article or legal concept β ranked leading cases + doctrine timeline |
generate_exam_question | Legal topic β real BGE fact pattern with hidden analysis for Fallbearbeitung practice |
get_law | Look up a federal or cantonal Swiss law by SR number / abbreviation + optional canton. Returns full article text from the local mirror (Fedlex for federal; cantonal_laws.db for cantonal β direct portal scrape for 22 cantons, LexFind PDF extraction for the remaining 4) |
search_laws | Unified federal + cantonal FTS5 search with BM25 rank interleaving. Filter by canton= for a specific jurisdiction, or jurisdiction='federal' / 'cantonal' |
get_commentary | Scholarly commentary for a specific federal law article from OnlineKommentar.ch (CC-BY-4.0) |
search_commentaries | Full-text search across 362 legal commentaries from OnlineKommentar.ch across 19 federal laws |
search_legislation | LexFind-backed discovery search across federal + cantonal legislation. fetch_top_n_texts=N enriches the top N results with full article text in a single call |
get_legislation | Full metadata + article text for any federal or cantonal law. Local-first (statutes.db β cantonal_laws.db β LexFind API fallback) |
browse_legislation_changes | Recent-changes feed per canton or federal level (live LexFind API, no local mirror) |
update_database | Re-download latest Parquet files from HuggingFace and rebuild the local database (local only) |
check_update_status | Check progress of a running database update (local only) |
OpenCaseLaw treats Swiss legal citations as a closed-corpus problem: every reference an LLM writes either resolves to a real entry in the ~1.05M-record corpus + Fedlex statute mirror, or it does not. The MCP server ships a four-tool toolkit that makes this contract enforceable end-to-end.
| Tool | Purpose |
|---|---|
cite | Build the canonical citation_string + canonical_url for a Swiss reference. Returns exists=false plus close_matches when the reference is fabricated β the LLM is contracted to copy the returned string verbatim instead of constructing one itself. |
check_claim_support | Per-claim Sonnet judge: given a (claim, decision_id, optional pinpoint), return supports: yes / partial / no / contradicts / unrelated against the verbatim ErwΓ€gung text. Different model family (Sonnet) than the one running retrieval (Haiku) so retrieval errors are not re-introduced in verification. |
attest_response | Closing audit, called once before a final answer ships. Runs up to five rails over the LLM's draft: β case β every BGE/BGer/BVGer/BStGer/BPatGer/MKGE reference exists, every pinpoint resolves; β‘ statute β every Art. X LAW reference resolves in statutes.db; β’ quote β every "β¦" of β₯30 chars appears verbatim in a cited source (decision or statute); β£ date β every vom DD.MM.YYYY adjacent to a citation matches the stored decision date; β€ grounding (opt-in via audit_grounding=true) β for each verified citation, an independent Sonnet judge checks whether the cited source actually supports the proposition the LLM attached to it. Returns linked_text ready to ship verbatim with every validated citation wrapped in a Markdown link. |
get_erwaegung / get_regeste / get_law / get_materialien / get_commentary | The verbatim-text suppliers β the only sources the LLM is permitted to direct-quote. |
The architecture defends against two empirically-measured legal-LLM failure classes:
The full server prompt (R1βR8) embeds these rules so any connecting client (Claude, ChatGPT, Cursor, Gemini, Copilot Studio) inherits the contract automatically. See mcp_server.py for the implementation, tests/web/test_attest_audits.py for the regression suite, and the verification section on opencaselaw.ch for a public explainer.
End-to-end performance against this architecture is measured by Swiss Legal RAG Bench β a benchmark modelled on Butler & Butler's Legal RAG Bench methodology, covering DE/FR/IT federal-law questions and decomposing errors into hallucination / retrieval / reasoning components. v0.1 baseline (live OpenCaseLaw + Claude Sonnet 4.6, 10 questions): 100 % correctness, 90 % groundedness, 70 % retrieval accuracy.
These work on both the remote and local server:
The AI calls the MCP tools automatically β you see the search results inline and can ask follow-up questions about specific decisions.
Four tools expose the reference graph: 9.65 million resolved decision-to-decision citation edges (10 million extracted) and 12.4 million statute references. These require the graph database (output/reference_graph.db); if it's not available, the tools return a message instead of failing.
find_citations β Given a decision, show its outgoing citations (what it references) and incoming citations (what references it). Each resolved citation includes the target decision's metadata and a confidence score. Unresolved references (e.g., older decisions not in the dataset) appear with their raw reference text.
Parameters: decision_id (required), direction (both/outgoing/incoming), min_confidence (0β1, default 0.3), limit (default 50, max 200).
find_appeal_chain β Trace the appeal chain (Instanzenzug) for a decision. Shows prior instances (lower courts) and subsequent instances (appeals to higher courts), reconstructing the full procedural path (e.g., Bezirksgericht β Obergericht β Bundesgericht).
Parameters: decision_id (required), min_confidence (0β1, default 0.3).
find_leading_cases β Find the most-cited decisions, ranked by how many other decisions reference them. Filter by statute (law code + article), text query, court, or date range.
Parameters: query (optional text), law_code + article (optional statute), court, date_from, date_to, limit (default 20, max 100). At least one of query or law_code is recommended; without any filter it returns the globally most-cited decisions.
analyze_legal_trend β Year-by-year decision counts showing how jurisprudence on a topic or statute has evolved over time. Returns a table with counts and a visual bar chart.
Parameters: query (optional text), law_code + article (optional statute), court, date_from, date_to. At least one of query or law_code is required.
Two tools provide direct access to Swiss law text from the local mirror, covering both federal and cantonal jurisdictions with article-level FTS5 indexing and sub-millisecond lookup:
statutes.db. Covers every consolidated federal act in German, French, and Italian.cantonal_laws.db, sourced via two layers:
get_law β Look up any Swiss law (federal or cantonal) by SR number or abbreviation, optionally fetching a specific article with full text.
Parameters: sr_number or abbreviation (at least one required), article (optional β omit to see the full article list), language (de/fr/it, default de), canton (default CH; set to ZH, BE, etc. for cantonal lookup).
search_laws β Unified FTS5 search across every Swiss statute article, federal and cantonal. BM25-ranked per corpus, merged by rank interleaving so each response surfaces both jurisdictions.
Parameters: query (required, FTS5 syntax), sr_number (optional β one specific federal law, implies federal-only), canton (optional β restrict to one canton, or CH for federal-only), jurisdiction (all / federal / cantonal, default all), language (de/fr/it), limit (1β50).
Three additional tools layer the live LexFind API on top of the local mirror for cases where the mirror isn't enough β newly published versions between refresh cycles, full version history, recent-changes feeds. These are the broader discovery surface; for plain "give me the current text" queries get_law / search_laws are faster and more reliable.
search_legislation β Broader discovery search across Swiss legislation, with optional single-call full-text enrichment. Useful when you don't know whether the law is federal or cantonal, or when you need a one-shot natural-language workflow.
Parameters: query (required), canton (optional β CH, ZH, BE, etc.), active_only (default true), search_in_content (default false β searches titles/keywords; set true to search law text), language (de/fr/it), limit (1β60, default 20), fetch_top_n_texts (0β10, default 0 β set to N to enrich top N results with parsed full article text).
get_legislation β Get details for a specific law including metadata, version history, and links to official sources (Fedlex, cantonal portals).
Parameters: lexfind_id (from search results) or systematic_number + canton (e.g., "220" + "CH"), include_versions (default false), language (de/fr/it).
browse_legislation_changes β Recent legislation changes for a canton or federal level. Shows new laws, amendments, and abrogations with dates and links.
Parameters: canton (default CH), language (de/fr/it).
Three tools support legal study covering the three core student workflows: understanding a case, understanding a doctrine, and practicing exam subsumption. Tools return rich structured data; Claude acts as the tutor and generates all pedagogy dynamically.
get_case_brief β Any case reference ("BGE 133 III 121", docket number, or decision_id) β structured brief. Returns regeste, Sachverhalt, key numbered ErwΓ€gungen, Dispositiv, cited statutes with text excerpts, citation authority (incoming/outgoing count), and the top cited-by and cites cases.
get_doctrine β Statute article ("Art. 41 OR") or legal concept ("culpa in contrahendo", "Tierhalterhaftung") β doctrine overview. Returns the leading cases ranked by citation count, a chronological doctrine timeline showing how rules evolved, and the statute text (if applicable).
generate_exam_question β Legal topic ("Haftpflichtrecht", "Art. 41 OR", "Mietrecht") β real Fallbearbeitung. Picks a leading BGE, returns the anonymized Sachverhalt as fact pattern plus a hidden analysis (applicable statutes, legal test, correct outcome). The student writes a subsumption, then asks Claude to reveal the analysis for comparison and feedback. Pass exclude_ids to avoid repeating the same case.
draft_mock_decision can use optional Fedlex URLs and caches fetched statute excerpts in
~/.swiss-caselaw/fedlex_cache.json (configurable via SWISS_CASELAW_FEDLEX_CACHE).
Use a fixed golden query set to track search relevance over time:
Metrics: MRR@k, Recall@k, nDCG@k, Hit@1
The repository also ships a frozen offline baseline at benchmarks/search_benchmark_2026-03-19_offline_full.json. On the full 100-query set against a 1,078,177-row local decisions.db, that run recorded MRR@10 = 0.4697, Recall@10 = 0.4958, nDCG@10 = 0.5250, and Hit@1 = 0.33. Treat it as a reproducible offline baseline rather than a fully provisioned hosted-deployment score.
You can enforce minimum quality gates (non-zero exit on failure):
For statute/citation-aware reranking, build the local graph database:
Then point the server to it:
Graph signals are enabled by default. To disable them, set SWISS_CASELAW_GRAPH_SIGNALS=0.
For statute lookup (get_law, search_laws), build the Fedlex statute database:
Then copy to the data directory:
Or set the path explicitly:
The full dataset is on HuggingFace as Parquet files β one file per court, 34 fields per decision including complete decision text.
Machine-consumable artifact metadata is published at artifacts/manifest.json. Besides daily Parquet/SQLite deltas, the manifest may point to an optional full compressed SQLite snapshot at artifacts/sqlite/snapshots/<date>.decisions.sqlite.zst for tools that want to bootstrap a local FTS5 database without rebuilding from Parquet.
Step 1. Install the library:
Step 2. Load the data:
Step 3. Explore:
Every court is a single Parquet file. Download directly:
Full list of files: huggingface.co/datasets/voilaj/swiss-caselaw/tree/main/data
Query the dataset over HTTP without installing anything. This uses the HuggingFace Datasets Server.
Get rows:
Get dataset info:
Search by SQL (DuckDB endpoint):
Note: The REST API queries the dataset as configured in the HuggingFace repo (per-court Parquet files, full 34-field schema). For bulk access or local analysis, use the download method above.
A local chat interface for searching Swiss court decisions. Ask questions in natural language, and an AI assistant searches the full corpus and answers with cited decisions.
Everything runs on your machine. No data leaves your computer (except LLM API calls to the provider you choose).
| Requirement | How to check | Where to get it |
|---|---|---|
| Python 3.10+ | python3 --version (macOS/Linux) or python --version (Windows) | python.org/downloads |
| Node.js 18+ | node --version | nodejs.org β download the LTS version |
| An LLM provider | (see below) | At least one cloud API key or a local model via Ollama |
| ~65 GB free disk | df -h . (macOS/Linux) | For the search index (downloaded on first run) |
Windows users: Install Python from python.org and check "Add Python to PATH" during installation. Node.js installs npm automatically.
| Provider | Env variable | Where to get a key | Cost |
|---|---|---|---|
| Google Gemini | GEMINI_API_KEY | aistudio.google.com/apikey | Free tier available |
| OpenAI | OPENAI_API_KEY | platform.openai.com/api-keys | Free credits for new accounts |
| Anthropic (Claude) | ANTHROPIC_API_KEY | console.anthropic.com | Pay-as-you-go |
Important: A Claude Desktop or Claude Pro subscription does NOT include an API key. You need a separate developer account at console.anthropic.com.
If you prefer not to use cloud APIs, you can run everything locally with Ollama:
| Model | Command to install | Download size | RAM needed |
|---|---|---|---|
| Qwen 2.5 (14B) | ollama pull qwen2.5:14b | ~9 GB | ~16 GB |
| Llama 3.3 (70B) | ollama pull llama3.3:70b | ~40 GB | ~48 GB |
Install Ollama from ollama.com (macOS, Linux, Windows), then:
The Web UI auto-detects Ollama and shows local models as available.
Step 1. Clone the repository:
Step 2. Create a Python virtual environment:
Activate it:
| OS | Command |
|---|---|
| macOS / Linux | source .venv/bin/activate |
| Windows (PowerShell) | .venv\Scripts\Activate.ps1 |
| Windows (cmd) | .venv\Scripts\activate.bat |
You'll know it's active when your terminal prompt starts with
(.venv).
Step 3. Install Python dependencies:
Then install at least one LLM provider SDK:
Tip: The
openaipackage is also used for local Ollama models (Ollama exposes an OpenAI-compatible API). If you only want to use local models,pip install openaiis sufficient β no cloud API key required.
Step 4. Install the frontend:
Step 5. Configure your API key:
Open .env in a text editor and paste your API key on the appropriate line. For example, if you have a Gemini key, change GEMINI_API_KEY=AI... to your actual key. Leave the other provider lines as-is β they will be ignored if empty.
You can also skip this step and configure keys from the Settings panel inside the UI after starting.
Step 6. Start the app:
| OS | Command |
|---|---|
| macOS / Linux | ./scripts/run_web_local.sh |
| Windows (PowerShell) | .\scripts\run_web_local.ps1 |
Open http://localhost:5173 in your browser.
What to expect on first run: The MCP server will automatically download the dataset (~7 GB) from HuggingFace and build a local search index (~58 GB). This takes 30β60 minutes depending on your connection and disk speed. You'll see progress in the terminal. After this one-time setup, the app starts instantly.
| Problem | Solution |
|---|---|
python3: command not found (Windows) | Use python instead of python3, or reinstall Python with "Add to PATH" checked |
npm: command not found | Install Node.js from nodejs.org |
ModuleNotFoundError: No module named 'fastapi' | Activate your venv (source .venv/bin/activate) and re-run pip install ... |
| "No provider configured" banner | Click the gear icon (Settings) and paste an API key, or start Ollama |
| "Database not found" on first run | Wait for the initial download to finish (check terminal for progress) |
| Port already in use | Edit BACKEND_PORT or FRONTEND_PORT in .env |
| PowerShell script blocked (Windows) | Run Set-ExecutionPolicy -Scope CurrentUser RemoteSigned once |
For advanced configuration (custom ports, MCP server path, timeouts), see .env.example.
Every decision has 34 structured fields:
| Field | Type | Example | Description |
|---|---|---|---|
decision_id | string | bger_6B_1234_2025 | Unique key: {court}_{docket_normalized} |
court | string | bger | Court code (full list) |
canton | string | CH | CH for federal, ZH/BE/GE/... for cantonal |
docket_number | string | 6B_1234/2025 | Original case number as published |
decision_date | date | 2025-03-15 | Date the decision was rendered |
language | string | de | de, fr, it, or rm |
full_text | string | (complete text) | Full decision text, typically 5β50 pages |
source_url | string | https://bger.ch/... | Permanent link to the original |
| Field | Type | Description |
|---|---|---|
regeste | string | Legal headnote / summary (Regeste) |
legal_area | string | Area of law (Strafrecht, Zivilrecht, ...) |
title | string | Subject line (Gegenstand) |
outcome | string | Result: Gutheissung, Abweisung, Nichteintreten, ... |
decision_type | string | Type: Urteil, Beschluss, VerfΓΌgung, ... |
cited_decisions | string | JSON array of cited decision references |
bge_reference | string | BGE collection reference if published |
abstract_de | string | German abstract (primarily BGE) |
abstract_fr | string | French abstract |
abstract_it | string | Italian abstract |
| Field | Type | Description |
|---|---|---|
chamber | string | Chamber (e.g., "I. zivilrechtliche Abteilung") |
judges | string | Panel composition |
clerks | string | Court clerks (Gerichtsschreiber) |
collection | string | Official collection reference |
appeal_info | string | Appeal status / subsequent proceedings |
| Field | Type | Description |
|---|---|---|
docket_number_2 | string | Secondary docket number |
publication_date | date | Date published online |
pdf_url | string | Direct URL to PDF |
external_id | string | Cross-reference ID |
scraped_at | datetime | When this decision was scraped |
source | string | Data source identifier |
source_id | string | Source-specific ID (e.g. Signatur) |
source_spider | string | Source spider/scraper name |
content_hash | string | MD5 of full_text for deduplication |
has_full_text | bool | Whether full_text is non-empty |
text_length | int | Character count of full_text |
Full 34-field Parquet export schema: export_parquet.py
| Court | Code | Decisions | Period | Source |
|---|---|---|---|---|
| Federal Supreme Court (BGer) | bger | ~173,000 | 1996βpresent | bger.ch |
| BGE Leading Cases | bge | ~45,000 | 1954βpresent | bger.ch CLIR |
| Federal Administrative Court (BVGer) | bvger | ~91,000 | 2007βpresent | bvger.ch |
| Federal Admin. Practice (VPB) | ch_vb | ~23,000 | 1982β2016 | admin.ch |
| Federal Criminal Court (BStGer) | bstger | ~11,000 | 2004βpresent | bstger.weblaw.ch |
| EDΓB (Data Protection) | edoeb | ~1,200 | 1994βpresent | edoeb.admin.ch |
| FINMA | finma | ~1,200 | 2008β2024 | finma.ch |
| ECHR (Swiss cases, BGer-published) | bge_egmr | ~475 | 1974βpresent | bger.ch CLIR |
| ECHR Switzerland (HUDOC) | hudoc_ch | 834 | 1959βpresent | HUDOC API |
| ECtHR Chamber judgments | ecthr_chamber | 193 (growing) | 1959βpresent (FR; EN v2) | HUDOC API |
| ECtHR Committee judgments | ecthr_committee | 30 (growing) | HUDOC API | |
| ECtHR Grand Chamber | ecthr_grand_chamber | 13 (growing) | HUDOC API | |
| MilitΓ€rkassationsgericht (MKG) | mkg | 1,244 | 1915β2025 | oa.admin.ch + alexandria.ch |
| Federal Patent Court (BPatGer) | bpatger | ~190 | 2012βpresent | bpatger.ch |
| Competition Commission (WEKO) | weko | ~120 | 2009βpresent | weko.admin.ch |
| Sports Tribunal | ta_sst | ~50 | 2024βpresent | ta-sst.ch |
| Federal Council | ch_bundesrat | ~15 | 2012βpresent | bj.admin.ch |
77 courts across all 26 cantons. The largest cantonal collections:
| Canton | Courts | Decisions | Period |
|---|---|---|---|
| Vaud (VD) | 3 | ~155,000 | 1984βpresent |
| ZΓΌrich (ZH) | 20 | ~126,000 | 1980βpresent |
| GenΓ¨ve (GE) | 1 | ~116,000 | 1993βpresent |
| Ticino (TI) | 1 | ~58,000 | 1995βpresent |
| St. Gallen (SG) | 7 | ~35,000 | 2001βpresent |
| GraubΓΌnden (GR) | 1 | ~29,000 | 2002βpresent |
| Basel-Landschaft (BL) | 1 | ~26,000 | 2000βpresent |
| Bern (BE) | 6 | ~26,000 | 2002βpresent |
| Aargau (AG) | 18 | ~21,000 | 1993βpresent |
| Basel-Stadt (BS) | 3 | ~19,000 | 2001βpresent |
All 26 cantons covered: AG, AI, AR, BE, BL, BS, FR, GE, GL, GR, JU, LU, NE, NW, OW, SG, SH, SO, SZ, TG, TI, UR, VD, VS, ZG, ZH.
Live per-court statistics: Dashboard
Scrape (01:00 UTC daily) β 45 scrapers run in parallel, each targeting a specific court's website or API. Every scraper is rate-limited and resumable: it tracks which decisions it has already seen and only fetches new ones. Output: one JSONL file per court. A separate Fedlex scraper downloads federal law texts (Akoma Ntoso XML) via SPARQL for the statute database.
Build search index (04:00 UTC) β JSONL files are ingested into a SQLite FTS5 database for full-text search. On MonβSat, this runs in incremental mode: a byte-offset checkpoint tracks how far each JSONL file has been read, so only newly appended decisions are processed (typically < 1 minute). On Sundays, a full rebuild compacts the FTS5 index and resets the checkpoint (~3 hours). Decisions appearing in multiple sources are deduplicated by decision_id, keeping the version with the longest full text. A quality enrichment step fills in missing titles, regestes, and content hashes.
Export β JSONL files are converted to Parquet (one file per court) with a fixed 34-field schema.
Upload β Parquet files are pushed to HuggingFace. The MCP server and datasets library pick up the new data automatically. Optional artifact publishing can also update artifacts/manifest.json with daily deltas and a full compressed SQLite snapshot for external bootstrap tools.
Update dashboard β stats.json is regenerated (including scraper health status from the last run) and pushed to GitHub Pages.
For contributors and developers who want to run scrapers, build the pipeline, or modify the codebase.
This installs all dependencies including PDF parsing, crypto, and the FastAPI server. For a minimal install without optional dependencies, use pip install -e . instead.
Output is written to output/decisions/{court}.jsonl β one JSON object per line, one file per court. The scraper remembers what it has already fetched (state stored in state/), so you can run it repeatedly to get only new decisions.
45 court codes are available. Run python run_scraper.py --list for the full list, or see the dashboard for per-court statistics.
This reads JSONL files from output/decisions/ and builds a SQLite FTS5 database at output/decisions.db. A full build of 900K decisions takes about 3 hours and produces a ~58 GB database. Incremental mode uses a checkpoint file (output/.fts5_checkpoint.json) to skip unchanged files and seek past already-processed bytes, completing in seconds when few new decisions exist.
Converts JSONL files to Parquet format (one file per court). Output goes to output/dataset/.
| Source | What | How |
|---|---|---|
| Official court websites | Federal courts (bger.ch, bvger.ch, bstger.ch, bpatger.ch) | JSON APIs, structured HTML |
| Federal regulatory bodies | FINMA, WEKO, EDΓB, VPB | Sitecore/custom APIs |
| Cantonal court portals | 26 cantonal platforms (Weblaw, Tribuna, FindInfo, custom portals) | Court-specific scrapers |
Decisions appearing in multiple sources are deduplicated by decision_id (a deterministic hash of court code + normalized docket number). The version with the longest full text is kept.
Court decisions are public records under Swiss law. Article 27 BGG requires the Federal Supreme Court to publish its decisions. The Bundesgericht has consistently held that court decisions must be made accessible to the public (BGE 133 I 106, BGE 139 I 129). This project scrapes only publicly available, officially published decisions.
Republication changes discoverability, so the project ships a governance policy covering source withdrawals, re-anonymization, and verified correction/removal requests. See docs/governance-and-removal-policy.md.
Maintainer: Jonas Hertner. A small group of first users, bug reporters, institutional partners, and consumer-side integrators have shaped the tool; see CONTRIBUTORS.md for the full list and how to add yours.
If you're using OpenCaseLaw in production β as a scraper contributor, dataset consumer, law-firm integrator, or research project β we'd like to know. Email team@jonashertner.com or open a discussion.
Code: MIT. See LICENSE.
Dataset packaging and added metadata: CC0-1.0 to the extent rights exist. The underlying decision texts remain official published court decisions sourced from the originating courts and public bodies.
Questions, feedback, or ideas? Reach out at team@jonashertner.com.
You can also open an issue on GitHub.
Showcase your server listing on GitHub or your project documentation. Embed this dynamic SVG badge to highlight official listing status and live engagement.
[](https://allmcps.com/mcp/swiss-case-law)<a href="https://allmcps.com/mcp/swiss-case-law"><img src="https://allmcps.com/api/badge/swiss-case-law?style=directory" alt="Swiss Case Law on AllMCPs" /></a>