OpenCaseLaw — Swiss Caselaw, Statutes & Doctrine

ch.opencaselaw由 opencaselaw.ch 發布官方發布者v1.3.0更新於 Nov 2, 2026

1M+ Swiss court decisions, statutes & doctrine with citation graph (DE/FR/IT). CC0, free, 42 tools.

已驗證Streamable HTTP可網頁執行網頁搜尋與抓取知識與記憶資料與分析

概覽

AI 產生的概覽

面向瑞士法律研究的遠端 MCP 伺服器:檢索百萬級法院裁判、法條與學說,並追蹤引用關係。

可以做什麼
  • 檢索並閱讀聯邦、邦及歐洲人權法院裁判,取得全文、Regeste 與結構化案件摘要
  • 追蹤裁判引用、審級鏈、指導性判例,以及某主題或法條的逐年趨勢
  • 查詢聯邦與邦法條原文,依主題或名稱檢索立法
  • 取得立法資料、學術評釋與開放取用文獻,並做反向引用查找
  • 核驗主張與引註是否與原文相符,產生考試題或模擬裁判大綱
適用情境
適合需要權威判例、法條原文或學說而非模型記憶的瑞士法律問答,也適合法學學習、引註查核以及聯邦或邦法研究。
執行需求
透過 streamable HTTP 連線 mcp.opencaselaw.ch 遠端端點,無需註冊、API 金鑰或請求標頭。README 另提供本機 Python 安裝方式,需下載約 7 GB 資料並建立約 58 GB 的 SQLite 索引,耗時 30 至 60 分鐘。
安裝前請注意
依 README 為唯讀且免費,但部分工具會將內容送往第三方:check_claim_support 使用獨立的 Sonnet 模型判定,立法探索工具呼叫即時 LexFind API。模擬裁判與考試題等輸出僅供研究或學習,不構成法律意見。

安裝

在 SourceWeft 中

  1. 開啟 儀表板中的 OpenCaseLaw — Swiss Caselaw, Statutes & Doctrine,將其新增到工作區。
  2. 為需要使用其工具的對話啟用該服務。

Web executable,透過 Streamable HTTP。 遠端服務在工作區中設定後即可從網頁執行環境執行。

交給代理程式安裝

把這段提示詞貼上到 Claude Code、Codex、Cursor 等代理程式,或 SourceWeft 對話中。代理程式會讀取該伺服器的安裝指南,先向你展示發布者、端點和登入方式,經你同意後再把它新增到你的客戶端。金鑰不會寫進任何命令。

閱讀 https://sourceweft.com/mcp/ch.opencaselaw%2Fswiss-caselaw/install.md,並按其中說明把這個 MCP 伺服器接入我的客戶端。修改任何設定之前,先告訴我它的發布者、端點和登入方式,等我確認。不要把 API 金鑰寫進命令或我會提交的檔案;問我金鑰應從哪裡取得。

檢視代理程式所依據的安裝指南

其他 MCP 客戶端

把它新增到你客戶端的 mcpServers 設定中。

{
  "mcpServers": {
    "opencaselaw-swiss-caselaw-statutes-doctr": {
      "type": "http",
      "url": "https://mcp.opencaselaw.ch/mcp"
    }
  }
}

README

OpenCaseLaw

Open Swiss case law and legislation for legal research and AI.

1M+ decisions · all 26 cantons · 1875–today · citation graph · MCP + REST + CLI + Parquet · CC0 data · MIT code

Try the search · Chat with your AI · Automate research · Download the dataset · API documentation

What you can do

  • Find Swiss decisions across federal and cantonal courts, in German, French and Italian.
  • Trace citations between cases and identify the leading decisions on a question.
  • Query case law and legislation from Claude, ChatGPT-compatible clients, or any MCP client.
  • Save repeatable research scripts, inspect citation resolution, and preserve selected evidence with the research CLI.
  • Download the corpus for legal NLP, retrieval and graph research.

Connect in 30 seconds

json
{  "mcpServers": {    "opencaselaw": {      "url": "https://mcp.opencaselaw.ch/mcp"    }  }}

No sign-up, no API key, read-only. 42 tools; setup guides for individual clients are at opencaselaw.ch/mcp.

Using Anthropic's Claude for Legal plugin? A Swiss playbook that makes its contract-review and NDA-triage skills work under Swiss law, every article verified against the statute mirror, is at opencaselaw.ch/claude/legal.local.md; see the setup guide.

Automate research

ocl is a small command-line client over the public research API, made for two jobs a chat window does badly: checking the citations in a draft, and keeping the evidence behind a memo in a folder you can share. It is on PyPI as opencaselaw-cli:

bash
pipx install opencaselaw-cli        # or: uv tool install opencaselaw-cliocl citations resolve 'BGE 136 III 513' '4A_747/2012' 'BGE 999 III 1'ocl bundle create 'Rachekündigung Art. 336 OR' --max-results 10 --passage 2 --law OR:336 --out evidence

The first prints, per reference, whether the decision exists, which one it is, and whether a requested Erwägung exists. The second saves the selected decisions, passages and statute articles into a folder with a plain-language INDEX.md and a hashed manifest.json. Everything comes back from the service unchanged: citation strings, passage text, source links. Search is bounded, and a citation check establishes existence, not that an authority supports a proposition. See the CLI guide.

Use MCP for conversations, the CLI or REST API for automation, and Parquet for corpus-scale analysis.

A real result

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:

bash
curl -sG https://mcp.opencaselaw.ch/api/decisions \  --data-urlencode "q=missbräuchliche Kündigung Rachekündigung" \  --data-urlencode "limit=3"

Coverage

ResourceCoverage
Court decisions1,050,000+ records, 118 court and source collections, 1875–today
LanguagesGerman, French, Italian (plus English, for the ECtHR judgments whose only authoritative text is English)
Legislation5,525 federal laws, 15,600 cantonal laws (all 26 cantons)
Citation graph~10M resolved decision-to-decision edges, 12.4M statute references
Interpretive layersFederal Council dispatches, commentaries, open-access scholarship, federal administrative practice
AccessWeb, MCP, REST, research CLI, daily CC0 Parquet
LicensingCC0 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.

The week, drawn

Every Friday the week in Swiss case law is drawn five ways, one design per language of the site: a relief map (German), 151 years in lines (French), a score (Italian), colour fields (Rumantsch) and a receipt (English). Same week, same numbers. The full archive is at opencaselaw.ch/posters.

[This week's poster in German: a relief map of new decisions by court seat] [This week's poster in French: one line per year of case law since 1875] [This week's poster in Italian: new decisions by canton and night] [This week's poster in Rumantsch: new decisions by language region] [This week's poster in English: new decisions itemised by court]

The posters are generated by scripts/weekly_poster_editions.py from the committed history of docs/stats.json, and are CC0 like the corpus.

Contributing

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.

Why this exists

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.

What you get

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:

  • All 7 federal courts (BGer, BVGer, BStGer, BPatGer, BGE, BGE historical, BGE-EGMR)
  • Federal military court (Militärkassationsgericht / MKG, 1,244 decisions 1915–2025)
  • All 26 cantonal court systems (first, second, and third instance)
  • Regulatory decisions (FINMA, ComCo, FDPIC, IndepBC, ElCom, PostCom, ComCom)
  • ECHR/EGMR: the Swiss-respondent material (487 BGE-published EGMR translations, 847 HUDOC Switzerland-tagged) plus every ECtHR Chamber and Grand Chamber judgment of importance 1–3 against any of the 46 respondent states — 8,275 judgments, in French where the Court published a French text and English otherwise. Importance 4 (low-importance and repetitive, which is the entire Committee docket) and admissibility decisions are out of scope.
  • Three official languages: German 463,012 (46.7%), French 446,869 (45.1%), Italian 81,796 (8.2%); schema reserves rm for Romansh
  • Deduplicated via docket normalisation + content-length-aware merge
  • Updated daily; BGer decisions available within ~15 minutes of court publication

Legislation — every Swiss law, federal and cantonal, locally mirrored with article-level indexing:

  • 5,525 federal laws / ~133,359 articles in each of DE/FR/IT from the Fedlex SPARQL endpoint
  • 15,600 cantonal laws / 361,430 articles, direct-scraped from all 26 cantons (LexWork covers 18, SIL covers 2, ZH OpenData covers ZH, TI~RL covers TI); LexFind PDF fallback supplements 4 cantons for laws missing from their primary portals
  • Unified SQLite FTS5 search federates both corpora; sub-millisecond article lookup
  • Monthly refresh on the 2nd of each month (the day after laws enter into force)
  • 12.4 M resolved links from decisions to individual statute articles

Citation graph — the only public large-scale citation graph of the Swiss legal system:

  • 9.65 M resolved decision-to-decision citation edges (10 M extracted) with confidence scores
  • 12.4 M decision-to-statute links resolved against the current consolidated text
  • Bidirectional lookup, appeal-chain resolution (Instanzenzug), leading-case ranking by citation authority
  • Powers 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:

  • Natural-language decision search (BM25 + synonym expansion + Haiku reranking, MRR@10 = 0.647 on a 100-query golden set)
  • Leading-case discovery, citation networks, appeal chains, jurisprudence evolution
  • Federal + cantonal law article lookup, full-text search across both — with colloquial→legal vocabulary expansion (searching "Vaterschaftsurlaub" finds the statute even though it says "Urlaub des andern Elternteils") and cross-language cantonal search (German query finds French/Italian cantonal laws)
  • Doctrine overviews (statute + authority-ranked BGEs + timeline + Botschaft reference)
  • Legislative history (Materialien) — 83,958 Botschaft amendment references across 9,139 BBl publications and 33,465 distinct (statute, article) pairs; verbatim Bundesblatt Materialien (19,800+ documents in DE/FR/IT, ~1.65M paragraphs: Federal Council Botschaften, Berichte and Stellungnahmen des Bundesrates, parliamentary committee reports, Erlassentwürfe; complete against Fedlex since 1999, 1850–1999 being added from the Federal Archives scans; 26,500+ links to the statute articles they concern) and every federal Vernehmlassung on Fedlex since 1993 (2,694 procedures with the FOJ legislative projects; 10,296 documents verbatim: Erläuternde Berichte, Vorlagen, Ergebnisberichte, Stellungnahmen — the latter on Fedlex systematically since 2016) accessible via search_botschaft (topical FTS5 across the verbatim corpus, filter by doc_type), get_materialien (the documents and consultations Fedlex links to a law, with the Erläuternder-Bericht passages on an article), 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.
  • Decision-structure access — get_decision_structure (Sachverhalt + Erwägungen + Dispositiv + Regeste split), get_erwaegung (verbatim Schweizer-citation Einheit, e.g. get_erwaegung("BGE 136 III 513", "2.3")), get_regeste (official BGer/BVGer/BStGer head-note)
  • Scholarly commentary lookup from OnlineKommentar.ch + OpenLegalCommentary.ch (1,131 commentaries)
  • Fallbearbeitung exam questions generated from real BGE fact patterns, with hidden analysis for practice
  • Draft mock decisions from fact patterns (research-only tool for grounding LLM outputs)
  • Structured case briefs (regeste, Sachverhalt, Erwägungen, Dispositiv, cited statutes, authority)

Multiple access paths — chat with your AI, automate research, or download the corpus:

For LLM users, researchers and developers:

  • Remote MCP server at mcp.opencaselaw.ch (SSE + Streamable HTTP) — 30-second setup in any MCP client (Claude, ChatGPT, Cursor, Gemini, Windsurf)
  • OpenAI-compatible tool definitions for Grok/xAI and any function-calling LLM API
  • Local MCP server — full offline capability, all 44 tools (the 2 local-only update tools are available offline), ~65 GB disk
  • Lightweight research CLI — JSON/JSONL, batch input, saved evidence bundles and citation resolution; pipx install opencaselaw-cli; source in clients/python/
  • REST API with interactive documentation (Swagger UI + OpenAPI spec)
  • Bulk Parquet download via the HuggingFace dataset (~7 GB)
  • Live dashboard + browsing UI at opencaselaw.ch

For legal practitioners drafting documents — curated practitioner surface:

  • Word add-in — Search decisions and insert correctly-formatted Swiss legal citations directly in Word (no copy-paste). Click an Erwägung to insert it with the correct sub-citation; click a law § to insert that alinea. Free Audit scans the whole document for fabricated citations and bad pinpoints (deterministic, no LLM). Pro tier (Stripe-billed, CHF 5/month, 25 AI calls/day shared, always-on client-side PII redaction — pattern-based, scope and limits at word.opencaselaw.ch/privacy.html) adds four AI tools: Verify (single citation vs. full-text decision), Strengthen (paragraph x-ray with leading-case suggestions ranked by citation-graph centrality + commentary excerpts), Find Support (locate decisions that back an unsupported statement, scored 0–100), and Reflect (literary mirror on the whole document — one canonical work that frames the same human dilemma + one reflective question). Exposes ~8 of the 44 MCP tools (42 remote in public mode + 2 local-only) through the REST API, tuned for the write-your-brief workflow.

Performance you can defend in a paper:

MetricValue
Decision search qualityMRR@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 resolution9.65 M edges resolved (10 M extracted)

What this is

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:

MethodFor whomWhat you get
Search with AIEveryoneNatural-language search in Claude, ChatGPT, Cursor, or Gemini — instant access, no download, full 24-tool surface
Citation AnalysisLegal scholars, researchersLeading cases, citation networks, appeal chains, jurisprudence trends over time
Statute LookupLegal professionalsFull article text from 5,525 federal laws and 15,600 cantonal laws, federated FTS5, sub-millisecond lookup
Legislation SearchLegal professionalsLexFind-backed discovery search with fetch_top_n_texts for single-call natural-language workflows
Education toolsLaw students, instructorsStructured case briefs, doctrine timelines, real-BGE exam questions with hidden analysis
Word Add-inLegal practitioners writing briefsInsert 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 / DownloadDevelopers, data scientists, NLP researchersResearch REST API, bulk Parquet download via HuggingFace (~7 GB)
Web UIEveryoneLive 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.


1. Search with AI

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 vs. local

RemoteLocal
Setup30 seconds30–60 minutes
DiskNone~65 GB
Tools38 (no local update tools)40, including update_database and check_update_status
FreshnessNightly (automatic)Manual
OfflineNoYes
RequiresClaude, ChatGPT, or Gemini CLI (see plans below)Any MCP client

Start with the remote server. Switch to local only if you need offline access.

Option A: Remote server (recommended)

Connect directly to the hosted MCP server — no data download, no local database, instant access to 1,050,000+ decisions.

What the hosted server records. Your queries are logged: tool, parameters including the query text, timestamp, client class and a rotating session identifier, and query text is kept indefinitely in an access-restricted private research archive used to improve search and train our own models. No IP address is stored with a query, there is no account or login, and the session identifier changes with every connection. Full details, in five languages: Privacy notice. If your queries are confidential — or you are sending them on behalf of clients — use Option B below and run the corpus locally, where nothing leaves your machine.

Claude.ai / Claude Desktop (easiest):

  1. Open Settings → Connectors
  2. Click "Add custom connector"
  3. Paste https://mcp.opencaselaw.ch
  4. Click Add

Same 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:

bash
claude mcp add swiss-caselaw --transport sse https://mcp.opencaselaw.ch
Alternative: manual JSON config (if custom connectors aren't available)

Add to claude_desktop_config.json (Node.js required):

json
{  "mcpServers": {    "swiss-caselaw": {      "command": "npx",      "args": ["-y", "mcp-remote", "https://mcp.opencaselaw.ch"]    }  }}

Restart Claude Desktop after saving.

ChatGPT:

  1. Open Settings → Apps → Advanced settings → enable Developer mode
  2. Click Create app → name it Swiss Caselaw, paste https://mcp.opencaselaw.ch/sse, auth: None
  3. In any chat: click + → Developer mode → select Swiss Caselaw

Available 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:

json
{  "mcpServers": {    "swiss-caselaw": {      "url": "https://mcp.opencaselaw.ch"    }  }}

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:

python
from google.adk.agents import LlmAgentfrom google.adk.tools import MCPToolsetfrom mcp.client.sse import SseConnectionParams
agent = LlmAgent(    model="gemini-3.1-pro",    name="swiss_law_agent",    instruction="You are a Swiss legal research assistant.",    tools=[        MCPToolset(            connection_params=SseConnectionParams(                url="https://mcp.opencaselaw.ch/sse",            ),        ),    ],)

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_database and check_update_status tools are only available on the local server — the remote dataset is updated automatically every night.

Option B: Local server (offline access)

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.

Setup with Claude Code

Claude Code is Anthropic's CLI for working with Claude in the terminal.

Step 1. Clone this repository:

bash
git clone https://github.com/jonashertner/opencaselaw.gitcd caselaw-repo-1

Step 2. Create a virtual environment and install the MCP server dependencies:

bash
python3 -m venv .venv
# macOS / Linuxsource .venv/bin/activatepip install mcp pydantic huggingface-hub pyarrow fastapi
# Windows (PowerShell).venv\Scripts\Activate.ps1pip install mcp pydantic huggingface-hub pyarrow

Step 3. Register the MCP server with Claude Code:

bash
# macOS / Linuxclaude mcp add swiss-caselaw -- /path/to/caselaw-repo-1/.venv/bin/python3 /path/to/caselaw-repo-1/mcp_server.py
# Windowsclaude mcp add swiss-caselaw -- C:\path\to\caselaw-repo-1\.venv\Scripts\python.exe C:\path\to\caselaw-repo-1\mcp_server.py

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:

  1. Downloads all Parquet files (~7 GB) from HuggingFace to ~/.swiss-caselaw/parquet/
  2. Builds a local SQLite FTS5 full-text search index at ~/.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).

Setup with Claude Desktop

See the Claude Desktop setup guide for step-by-step instructions (macOS + Windows).

Quick version — add this to your claude_desktop_config.json:

json
{  "mcpServers": {    "swiss-caselaw": {      "command": "/path/to/caselaw-repo-1/.venv/bin/python3",      "args": ["/path/to/caselaw-repo-1/mcp_server.py"]    }  }}

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.

Keeping the data current

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.

SQLite snapshot artifacts

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:

bash
pip install zstandard
python - <<'PY'import hashlibimport jsonimport osimport sqlite3import urllib.requestfrom pathlib import Path
import zstandard as zstd
base = "https://huggingface.co/datasets/voilaj/swiss-caselaw/resolve/main"data_dir = Path(os.environ.get("SWISS_CASELAW_DIR", Path.home() / ".swiss-caselaw")).expanduser()data_dir.mkdir(parents=True, exist_ok=True)
with urllib.request.urlopen(f"{base}/artifacts/manifest.json") as r:    manifest = json.load(r)
snapshot = manifest.get("snapshot")if not snapshot:    raise SystemExit("No SQLite snapshot is advertised; use update_database instead.")
meta = snapshot["sqlite_zst"]tmp_db = data_dir / "decisions.db.tmp"final_db = data_dir / "decisions.db"
class HashingReader:    def __init__(self, raw, digest):        self.raw = raw        self.digest = digest
    def read(self, size=-1):        data = self.raw.read(size)        if data:            self.digest.update(data)        return data
h = hashlib.sha256()with urllib.request.urlopen(f"{base}/{meta['path']}") as src, tmp_db.open("wb") as dst:    zstd.ZstdDecompressor().copy_stream(HashingReader(src, h), dst)if h.hexdigest() != meta["sha256"]:    tmp_db.unlink(missing_ok=True)    raise SystemExit("SHA-256 verification failed")
con = sqlite3.connect(tmp_db)try:    rows = con.execute("SELECT COUNT(*) FROM decisions").fetchone()[0]    con.execute("SELECT decision_id FROM decisions LIMIT 1").fetchone()finally:    con.close()
expected = snapshot.get("rows")if expected is not None and rows != expected:    raise SystemExit(f"Row-count check failed: got {rows}, expected {expected}")
tmp_db.replace(final_db)print(f"Installed {final_db} with {rows:,} decisions")PY
How the local database works
~/.swiss-caselaw/├── parquet/              # Downloaded Parquet files from HuggingFace (~7 GB)│   └── data/│       ├── bger.parquet│       ├── bvger.parquet│       └── ...           # 100 files, one per court├── decisions.db          # SQLite FTS5 search index (~58 GB)├── reference_graph.db    # Case citations + statute links (~3.5 GB)└── statutes.db           # Federal law articles from Fedlex (~610 MB)

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:

  1. Detects query intent — docket number lookup (6B_1234/2023), explicit FTS syntax (Mietrecht AND Kündigung), or natural language (decisions on tenant eviction).

  2. 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.

  3. 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.

  4. Reranks with signal scoring — The top candidates are reranked using a composite score that combines:

    • BM25 score (from FTS5, with custom column weights: full_text 1.2, regeste 5.0, title 6.0 — headnotes and titles are weighted heavily over body text)
    • Term coverage in title (3.0x), regeste (2.2x), and snippet (0.8x)
    • Phrase match in title/regeste (1.8x)
    • Docket match — exact (6.0x) or partial (2.0x)
    • Statute/citation graph signals — if the query mentions an article (e.g., "Art. 8 BV"), decisions that cite that provision are boosted
    • Court prior — e.g., asylum queries boost BVGer results
  5. 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 tools

Available on both remote and local unless noted.

ToolDescription
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
find_leading_casesFind the most-cited decisions for a topic or statute
analyze_legal_trendYear-by-year decision counts for a statute or topic
draft_mock_decisionBuild a research-only mock decision outline from facts, grounded in caselaw + statute references; asks clarification questions before conclusion
get_case_briefStructured brief for any case — regeste, key Erwägungen, cited statutes, and citation authority
get_doctrineStatute article or legal concept → ranked leading cases + doctrine timeline
generate_exam_questionLegal topic → real BGE fact pattern with hidden analysis for Fallbearbeitung practice
get_lawLook 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_lawsUnified federal + cantonal FTS5 search with BM25 rank interleaving. Filter by canton= for a specific jurisdiction, or jurisdiction='federal' / 'cantonal'
get_commentaryScholarly commentary for a specific federal law article from OnlineKommentar.ch (CC-BY-4.0)
search_commentariesFull-text search across 362 legal commentaries from OnlineKommentar.ch across 19 federal laws
search_legislationLexFind-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_legislationFull metadata + article text for any federal or cantonal law. Local-first (statutes.db → cantonal_laws.db → LexFind API fallback)
browse_legislation_changesRecent-changes feed per canton or federal level (live LexFind API, no local mirror)
update_databaseRe-download latest Parquet files from HuggingFace and rebuild the local database (local only)
check_update_statusCheck progress of a running database update (local only)

Citation-integrity toolkit (anti-hallucination)

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.

ToolPurpose
citeBuild 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_supportPer-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_responseClosing 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_commentaryThe verbatim-text suppliers — the only sources the LLM is permitted to direct-quote.

The architecture defends against two empirically-measured legal-LLM failure classes:

  • Hallucination (Dahl, Magesh, Suzgun & Ho, Large Legal Fictions, Stanford RegLab, 2024 — Journal of Legal Analysis): 58–88 % of legal queries to general-purpose LLMs produced at least one fabricated authority. A follow-up Stanford RegLab study of commercial legal-RAG tools (Magesh et al., Hallucination-Free?, 2024) measured 17–33 %. → caught by audits ① ② ③ ④.
  • Reasoning error (Butler & Butler, Isaacus, Legal RAG Bench, Mar 2026): citation is real and source was retrieved, but the proposition attached to it is not actually supported by the cited text. → caught by audit ⑤.

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.

Example queries

These work on both the remote and local server:

> Search for BGer decisions on Mietrecht Kündigung from 2024
> What did the BVGer rule on asylum seekers from Eritrea?
> Show me the full text of 6B_1234/2023
> How many decisions does each court in canton Zürich have?
> Find decisions citing Art. 8 BV
> What are the leading cases on Art. 8 EMRK?
> Show me the citation network for BGE 138 III 374
> How has Mietrecht jurisprudence evolved over time?
> Show me Art. 41 OR (statute lookup)
> Search for statute provisions about Verjährung
> Trace the appeal chain for 5A_234/2026
> Explain BGE 133 III 121 to me
> What are the leading cases on Art. 41 OR and how has the doctrine evolved?
> Give me a practice case on Haftpflichtrecht
> Search for cantonal data protection laws
> Show me the details of SR 220 (Obligationenrecht)
> What legislation changed recently in Zürich?

The AI calls the MCP tools automatically — you see the search results inline and can ask follow-up questions about specific decisions.

Citation graph tools

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.

> Show me the citation network for BGE 138 III 374
## Outgoing (13 — what this decision cites)1. BGE 136 III 365 (2010-01-01) [bge] conf=0.98 mentions=12. BGE 133 III 189 (2007-01-01) [bge] conf=0.93 mentions=1...
## Incoming (13,621 — what cites this decision)1. 5A_234/2026 (2026-02-19) [bger] conf=0.92 mentions=22. 5A_117/2026 (2026-01-30) [bger] conf=0.88 mentions=1...

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.

> What are the leading cases on Art. 8 EMRK?
1. BGE 135 I 143 (2009) — 3,840 citations   Regeste: Anspruch auf Aufenthaltsbewilligung...2. BGE 130 II 281 (2004) — 3,155 citations   Regeste: Familiennachzug; gefestigtes Anwesenheitsrecht...

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.

> How has Art. 29 BV jurisprudence evolved?
**Filter:** Art. 29 BV**Total:** 34,669 decisions
Year     Count  Bar2000       275  █████2005       733  ████████████2010     1,028  █████████████████2015     1,536  ██████████████████████████2020     1,896  ████████████████████████████████2025     2,400  ████████████████████████████████████████

Parameters: query (optional text), law_code + article (optional statute), court, date_from, date_to. At least one of query or law_code is required.

Statute lookup tools (local, federal + cantonal)

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:

  • Federal: 5,525 laws / ~133,359 articles in each of DE/FR/IT from the Fedlex SPARQL endpoint, mirrored monthly into statutes.db. Covers every consolidated federal act in German, French, and Italian.
  • Cantonal: 15,600 laws / 361,430 articles in cantonal_laws.db, sourced via two layers:
    • Direct portal scraping (primary) — 22 cantons whose official Gesetzessammlungen are published via the LexWork, SIL, ZH OpenData, or TI Raccolta delle Leggi platforms (AG, AI, AR, BE, BL, BS, FR, GE, GL, GR, LU, NE, NW, OW, SG, SH, SO, TG, TI, VS, ZG, ZH). The HTML is parsed natively — no PDF extraction, no OCR — yielding clean article-level data.
    • LexFind PDF fallback — 4 cantons not yet covered by direct scrapers (JU, SZ, UR, VD). PDFs from LexFind.ch are extracted with PyMuPDF and segmented into articles. Bilingual secondary-language passes for BE / FR / VS / GR.
    • Combined and federated via SQLite FTS5 with the federal table.

get_law — Look up any Swiss law (federal or cantonal) by SR number or abbreviation, optionally fetching a specific article with full text.

> Show me Art. 8 BV
# BV — SR 101**Bundesverfassung der Schweizerischen Eidgenossenschaft vom 18. April 1999**
### Art. 8 — Rechtsgleichheit1 Alle Menschen sind vor dem Gesetz gleich.2 Niemand darf diskriminiert werden, namentlich nicht wegen der Herkunft, ...
> Show me Art. 1 of the Zurich Hundegesetz
get_law(canton="ZH", sr_number="554.5", article="1")

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.

> Search for statute provisions about Verjährung
1. [CH]  Art. 130 OR (SR 220): Die Verjährung beginnt mit der Fälligkeit der Forderung...2. [ZH]  § 19 Kirchgemeindenreglement (SR 182.60): ... Verjährungsbestimmungen ...3. [CH]  Art. 132 OR (SR 220): Bei der Berechnung der Frist ist der Tag...

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).

Legislation discovery tools (LexFind-backed)

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.

> Search for the Bernese dog act with full text
search_legislation(query="Hundegesetz", canton="BE", fetch_top_n_texts=2)→ returns top 2 matches with full_text_preview + sample_articles inline,  no follow-up get_legislation call needed.

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).

> Show me the Obligationenrecht on LexFind
SR 220 — Bundesgesetz betreffend die Ergänzung des SchweizerischenZivilgesetzbuches (Fünfter Teil: Obligationenrecht)Entity: Bund (CH) | Category: Gesetz | Keywords: ORIn force since: 01.01.2026Sources:  DE: https://www.fedlex.admin.ch/eli/cc/27/317_321_377/de  FR: https://www.fedlex.admin.ch/eli/cc/27/317_321_377/fr

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.

> What legislation changed recently in Zürich?
1. [01.03.2026] version — Vollzugsverordnung zur Finanzverordnung (SR 181.131)2. [01.03.2026] new — Studienordnung für den Masterstudiengang... (SR 414.253.125)3. [01.03.2026] version — Organisationsverordnung der Finanzdirektion (SR 172.110.3)

Parameters: canton (default CH), language (de/fr/it).

Education tools

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).

Search quality benchmark

Use a fixed golden query set to track search relevance over time:

bash
python3 benchmarks/run_search_benchmark.py \  --db ~/.swiss-caselaw/decisions.db \  -k 10 \  --json-output benchmarks/latest_search_benchmark.json

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):

bash
python3 benchmarks/run_search_benchmark.py \  --db ~/.swiss-caselaw/decisions.db \  -k 10 \  --min-mrr 0.50 \  --min-recall 0.75 \  --min-ndcg 0.85

Build Reference Graph (Optional)

For statute/citation-aware reranking, build the local graph database:

bash
python3 search_stack/build_reference_graph.py \  --source-db ~/.swiss-caselaw/decisions.db \  --courts bger,bge,bvger \  --db output/reference_graph.db

Then point the server to it:

bash
export SWISS_CASELAW_GRAPH_DB=output/reference_graph.db

Graph signals are enabled by default. To disable them, set SWISS_CASELAW_GRAPH_SIGNALS=0.

Build Statutes Database (Optional)

For statute lookup (get_law, search_laws), build the Fedlex statute database:

bash
# Download the top 100 most-cited federal laws from Fedlexpython3 -m scrapers.fedlex --top 100
# Build the SQLite FTS5 statutes databasepython3 -m search_stack.build_statutes_db

Then copy to the data directory:

bash
cp output/statutes.db ~/.swiss-caselaw/

Or set the path explicitly:

bash
export SWISS_CASELAW_STATUTES_DB=output/statutes.db

2. Download the dataset

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.

With Python (datasets library)

Step 1. Install the library:

bash
pip install datasets

Step 2. Load the data:

python
from datasets import load_dataset
# Load a single court (~170k decisions, ~800 MB)bger = load_dataset("voilaj/swiss-caselaw", data_files="data/bger.parquet")
# Load all courts (~900K decisions, ~6.5 GB download)ds = load_dataset("voilaj/swiss-caselaw", data_files="data/*.parquet")

Step 3. Explore:

python
# Print a single decisiondecision = bger["train"][0]print(decision["docket_number"])   # "6B_1/2024"print(decision["decision_date"])   # "2024-03-15"print(decision["language"])        # "de"print(decision["regeste"][:200])   # First 200 chars of the headnoteprint(decision["full_text"][:500]) # First 500 chars of the full text

With pandas

python
import pandas as pd
# Load one courtdf = pd.read_parquet("hf://datasets/voilaj/swiss-caselaw/data/bger.parquet")
# Filter by datedf_recent = df[df["decision_date"] >= "2024-01-01"]print(f"{len(df_recent)} decisions since 2024")
# Filter by languagedf_french = df[df["language"] == "fr"]
# Group by legal areadf.groupby("legal_area").size().sort_values(ascending=False).head(10)

Direct download

Every court is a single Parquet file. Download directly:

https://huggingface.co/datasets/voilaj/swiss-caselaw/resolve/main/data/bger.parquethttps://huggingface.co/datasets/voilaj/swiss-caselaw/resolve/main/data/bvger.parquethttps://huggingface.co/datasets/voilaj/swiss-caselaw/resolve/main/data/zh_gerichte.parquet...

Full list of files: huggingface.co/datasets/voilaj/swiss-caselaw/tree/main/data


3. REST API

Query the dataset over HTTP without installing anything. This uses the HuggingFace Datasets Server.

Get rows:

bash
curl "https://datasets-server.huggingface.co/rows?dataset=voilaj/swiss-caselaw&config=default&split=train&offset=0&length=5"

Get dataset info:

bash
curl "https://datasets-server.huggingface.co/info?dataset=voilaj/swiss-caselaw"

Search by SQL (DuckDB endpoint):

bash
curl -X POST "https://datasets-server.huggingface.co/search?dataset=voilaj/swiss-caselaw&config=default&split=train" \  -d '{"query": "SELECT docket_number, decision_date, language FROM data WHERE court = '\''bger'\'' LIMIT 10"}'

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.


4. Web UI

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.

Browser (localhost:5173)  →  FastAPI backend  →  MCP server  →  Local SQLite FTS5 DB

Everything runs on your machine. No data leaves your computer (except LLM API calls to the provider you choose).

What you need

RequirementHow to checkWhere to get it
Python 3.10+python3 --version (macOS/Linux) or python --version (Windows)python.org/downloads
Node.js 18+node --versionnodejs.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 diskdf -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.

Cloud providers (choose at least one, or use Ollama below)
ProviderEnv variableWhere to get a keyCost
Google GeminiGEMINI_API_KEYaistudio.google.com/apikeyFree tier available
OpenAIOPENAI_API_KEYplatform.openai.com/api-keysFree credits for new accounts
Anthropic (Claude)ANTHROPIC_API_KEYconsole.anthropic.comPay-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.

Local models (no API key needed)

If you prefer not to use cloud APIs, you can run everything locally with Ollama:

ModelCommand to installDownload sizeRAM 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:

bash
ollama serve          # start the Ollama server (leave running)ollama pull qwen2.5:14b   # download a model (one-time)

The Web UI auto-detects Ollama and shows local models as available.

Step-by-step setup

Step 1. Clone the repository:

bash
git clone https://github.com/jonashertner/opencaselaw.gitcd caselaw-repo-1

Step 2. Create a Python virtual environment:

bash
python3 -m venv .venv

Activate it:

OSCommand
macOS / Linuxsource .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:

bash
pip install fastapi uvicorn python-dotenv mcp pyarrow pydantic

Then install at least one LLM provider SDK:

bash
pip install anthropic        # for Claudepip install openai           # for OpenAI / GPT-4o / local models via Ollamapip install google-genai     # for Google Gemini

Tip: The openai package is also used for local Ollama models (Ollama exposes an OpenAI-compatible API). If you only want to use local models, pip install openai is sufficient — no cloud API key required.

Step 4. Install the frontend:

bash
cd web_ui && npm install && cd ..

Step 5. Configure your API key:

bash
cp .env.example .env

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:

OSCommand
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.

Features

  • 5 models: Claude, OpenAI, Gemini (cloud) + Qwen 2.5 and Llama 3.3 via Ollama (local)
  • Local-first option: Run entirely on your machine with Ollama — no cloud API keys needed
  • Streaming: Responses appear token-by-token in real time
  • Tool-augmented chat: The AI calls search, get_decision, list_courts, etc. automatically
  • Decision cards: Clickable statute references with inline Fedlex article text
  • Filters: Narrow results by court, canton, language, and date range
  • In-app settings: Configure API keys and Ollama connection from the UI
  • Export: Download conversations as Markdown, Word, or PDF

Troubleshooting

ProblemSolution
python3: command not found (Windows)Use python instead of python3, or reinstall Python with "Add to PATH" checked
npm: command not foundInstall 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" bannerClick the gear icon (Settings) and paste an API key, or start Ollama
"Database not found" on first runWait for the initial download to finish (check terminal for progress)
Port already in useEdit 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.


What's in each decision

Every decision has 34 structured fields:

Core fields

FieldTypeExampleDescription
decision_idstringbger_6B_1234_2025Unique key: {court}_{docket_normalized}
courtstringbgerCourt code (full list)
cantonstringCHCH for federal, ZH/BE/GE/... for cantonal
docket_numberstring6B_1234/2025Original case number as published
decision_datedate2025-03-15Date the decision was rendered
languagestringdede, fr, it, or rm
full_textstring(complete text)Full decision text, typically 5–50 pages
source_urlstringhttps://bger.ch/...Permanent link to the original

Legal content

FieldTypeDescription
regestestringLegal headnote / summary (Regeste)
legal_areastringArea of law (Strafrecht, Zivilrecht, ...)
titlestringSubject line (Gegenstand)
outcomestringResult: Gutheissung, Abweisung, Nichteintreten, ...
decision_typestringType: Urteil, Beschluss, Verfügung, ...
cited_decisionsstringJSON array of cited decision references
bge_referencestringBGE collection reference if published
abstract_destringGerman abstract (primarily BGE)
abstract_frstringFrench abstract
abstract_itstringItalian abstract

Court metadata

FieldTypeDescription
chamberstringChamber (e.g., "I. zivilrechtliche Abteilung")
judgesstringPanel composition
clerksstringCourt clerks (Gerichtsschreiber)
collectionstringOfficial collection reference
appeal_infostringAppeal status / subsequent proceedings

Technical fields

FieldTypeDescription
docket_number_2stringSecondary docket number
publication_datedateDate published online
pdf_urlstringDirect URL to PDF
external_idstringCross-reference ID
scraped_atdatetimeWhen this decision was scraped
sourcestringData source identifier
source_idstringSource-specific ID (e.g. Signatur)
source_spiderstringSource spider/scraper name
content_hashstringMD5 of full_text for deduplication
has_full_textboolWhether full_text is non-empty
text_lengthintCharacter count of full_text

Full 34-field Parquet export schema: export_parquet.py


Coverage

Federal courts

CourtCodeDecisionsPeriodSource
Federal Supreme Court (BGer)bger~173,0001996–presentbger.ch
BGE Leading Casesbge~45,0001954–presentbger.ch CLIR
Federal Administrative Court (BVGer)bvger~91,0002007–presentbvger.ch
Federal Admin. Practice (VPB/JAAC)ch_vb2,5021987–2017amtsdruckschriften.bar.admin.ch (Bundesarchiv; frozen, the series ended 2018)
Bundesblatt, Amtliches Bulletin and Amtliche Sammlung pages (not rulings: dates unreliable, all tagged language=de though many are French or Italian, cited as "Bundesverwaltung <docket>")ch_vb~20,400mixedsame archive (frozen; restored 2026-10-31)
Federal Criminal Court (BStGer)bstger~11,0002004–presentbstger.weblaw.ch
EDÖB (Data Protection)edoeb~1,2001994–presentedoeb.admin.ch
FINMAfinma~1,2002008–2024finma.ch
ECHR (Swiss cases, BGer-published)bge_egmr~4751974–presentbger.ch CLIR
ECHR Switzerland (HUDOC)hudoc_ch8341959–presentHUDOC API
ECtHR Chamber judgmentsecthr_chamber7,7611960–present (FR or EN)HUDOC API
ECtHR Grand Chamberecthr_grand_chamber5141960–present (FR or EN)HUDOC API
ECtHR Committee judgmentsecthr_committee0 — importance 4, out of scopeHUDOC API
Militärkassationsgericht (MKG)mkg1,2441915–2025oa.admin.ch + alexandria.ch
Federal Patent Court (BPatGer)bpatger~1902012–presentbpatger.ch
Competition Commission (WEKO)weko~1202009–presentweko.admin.ch
Sports Tribunalta_sst~502024–presentta-sst.ch
Federal Councilch_bundesrat~152012–presentbj.admin.ch

Cantonal courts

77 courts across all 26 cantons. The largest cantonal collections:

CantonCourtsDecisionsPeriod
Vaud (VD)3~155,0001984–present
Zürich (ZH)20~126,0001980–present
Genève (GE)1~116,0001993–present
Ticino (TI)1~58,0001995–present
St. Gallen (SG)7~35,0002001–present
Graubünden (GR)1~29,0002002–present
Basel-Landschaft (BL)1~26,0002000–present
Bern (BE)6~26,0002002–present
Aargau (AG)18~21,0001993–present
Basel-Stadt (BS)3~19,0002001–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


How it works

                        ┌──────────────────────────────────────────────────────┐                        │                    Daily Pipeline                    │                        │                                                      │Court websites ────────►│  Scrapers ──► JSONL ──┬──► Parquet ──► HuggingFace   │  bger.ch               │  (45 scrapers,        │                              │  bvger.ch              │   rate-limited,        ├──► FTS5 DB ───┐             │  cantonal portals      │   resumable)           │               ├► MCP Server │                        │                        └──► Graph DB ──┘             │                        │                                          ▲           │Fedlex (SPARQL) ───────►│  Fedlex scraper ──► XML ──► Statutes DB ─┘           │                        │                                                      │                        │  01:00 UTC  scrape        04:00 UTC  publish         │                        └──────────────────────────────────────────────────────┘

Step by step

  1. 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.

  2. 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.

  3. Export — JSONL files are converted to Parquet (one file per court) with a fixed 34-field schema.

  4. 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.

  5. Update dashboard — stats.json is regenerated (including scraper health status from the last run) and pushed to GitHub Pages.


Running locally (developer)

For contributors and developers who want to run scrapers, build the pipeline, or modify the codebase.

Prerequisites

  • Python 3.10+
  • pip

Install

bash
git clone https://github.com/jonashertner/opencaselaw.gitcd caselaw-repo-1python3 -m venv .venvsource .venv/bin/activate   # macOS/Linux — on Windows: .venv\Scripts\Activate.ps1pip install -e ".[all]"

This installs all dependencies including PDF parsing, crypto, and the FastAPI server. For a minimal install without optional dependencies, use pip install -e . instead.

Scrape decisions

bash
# Scrape 5 recent decisions from the Federal Supreme Courtpython run_scraper.py bger --max 5 -v
# Scrape BVGer decisions since a specific datepython run_scraper.py bvger --since 2025-01-01 --max 20 -v
# Scrape a cantonal courtpython run_scraper.py zh_gerichte --max 10 -v

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.

Build a local search database

bash
# Full build (reads all JSONL, optimizes FTS index — ~3h for 900K decisions)python build_fts5.py --output output -v
# Incremental build (reads only new JSONL bytes, skips optimize — seconds)python build_fts5.py --output output --incremental --no-optimize -v
# Full rebuild (deletes DB + checkpoint, rebuilds from scratch)python build_fts5.py --output output --full-rebuild -v

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.

Export to Parquet

bash
python export_parquet.py --input output/decisions --output output/dataset -v

Converts JSONL files to Parquet format (one file per court). Output goes to output/dataset/.


Data sources

SourceWhatHow
Official court websitesFederal courts (bger.ch, bvger.ch, bstger.ch, bpatger.ch)JSON APIs, structured HTML
Federal regulatory bodiesFINMA, WEKO, EDÖB, VPBSitecore/custom APIs
Cantonal court portals26 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.


Legal basis

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.

Governance and removal

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.


Contributors

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 [email protected] or open a discussion.


License

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.


Contact

Questions, feedback, or ideas? Reach out at [email protected].

You can also open an issue on GitHub.

Project updates are posted on LinkedIn.

來源:README.md,提交 f41f77c

工具

42

fetch

Fetch decision (basic)

Minimal fetch tool following the deep-research search/fetch convention. Fetches one Swiss decision's full text by id, returning {id, title, text, url, metadata}. The id comes from a `search` result (a decision_id like bger_6B_1234_2025; a docket number or BGE reference also resolves). General clients should prefer `get_decision`.

輸入參數id必填

search_decisions

Search decisions

Use this tool to find COURT DECISIONS (Rechtsprechung): 1,050,000+ Swiss federal and cantonal decisions plus ~2,800 ECtHR decisions concerning Switzerland. Full-text search with keywords, phrases (in quotes), Boolean operators (AND, OR, NOT), prefix matching (word*), docket lookup (6B_1234/2025) and column-scoped search (regeste:keyword). Filter by court, canton, language, date range, chamber. Results are relevance-ranked and enriched with citation counts, statute references and is_leading_case. Use offset to page. Most recent decisions: empty query + sort='date_desc' + court filter. Newly PUBLISHED decisions (alerts): published_from + sort='published_desc'. Not for statutes (search_laws), commentary (search_scholarship), or administrative guidance (search_practice).

輸入參數sortcourtlimitquerycantonfieldsoffsetchamberdate_tolanguagedate_frompublished_to另有 3 個

get_decision

Get decision

Fetch a single court decision with full text. Look up by decision_id (e.g., bger_6B_1234_2025), docket number (e.g., 6B_1234/2025), or partial match. Full text is truncated at 200,000 characters for very long decisions. Set full_text=false to get only metadata and regeste.

輸入參數full_textdecision_id必填

get_decisions

Get decisions (batch)

Use this tool to fetch SEVERAL decisions at once (1-10 ids) instead of calling get_decision repeatedly. Same data per decision — canonical citation strings, Markdown link, Regeste, metadata — in ONE call. Prefer this whenever you are about to read more than one decision from a search result: it is the difference between one tool call and ten, which matters because clients cap tool calls per turn. full_text defaults to FALSE here (unlike get_decision) because ten full judgments exceed connector response limits; set it true only for a short id list, and each text is excerpted with the cut disclosed. Ids not found are reported individually — the rest of the batch still returns.

輸入參數full_textdecision_ids必填max_chars_per_decision

list_courts

List courts

List all available courts with decision counts, date ranges, and language coverage. Use this to discover what data is available.

無需輸入參數。

get_statistics

Corpus statistics

Get aggregate statistics about the dataset. Optionally filter by court, canton, or year.

輸入參數yearcourtcanton

find_citations

Find citations

Given a decision_id, show what it cites and what cites it. Uses the reference graph database with 9.65M citation edges. Returns resolved citations with confidence scores and unresolved references.

輸入參數limitoffsetdirectiondecision_id必填min_confidence

find_appeal_chain

Find appeal chain

Trace the appeal chain (Instanzenzug) for a decision. Shows prior instances (lower courts) and subsequent instances (appeals to higher courts). Reconstructs the full procedural path, e.g. Bezirksgericht → Obergericht → Bundesgericht. Uses the is_prior_instance flag from decision headers.

輸入參數decision_id必填min_confidence

find_leading_cases

Find leading cases

Find the most-cited decisions for a topic or statute. Authority ranking based on citation graph; a free-text topic ranks decisions whose regeste carries the query terms first (`regeste_match`: share of terms found, null without a regeste). Filter by statute (law_code + article), topic query, court, and date range. Top-3 results auto-attach a `pinpoint` field {e_number, matched_sentence, confidence, url, score, source} anchoring the most-relevant Erwägung (surface it as 'E. <e_number>' when confidence is high or medium). Each result also carries citation_string_{de,fr,it} + canonical_url + is_leading_case + citation_count for ready-to-quote Swiss-format citations. A statute reference inside `query` ("Art. 29 BV") is used as the statute filter. When the query excludes every candidate, the page is relaxed to ranked OR and flagged `filters_relaxed: true` with a `note` — nearest neighbours, not on-point authority.

輸入參數courtlimitqueryarticledate_tolaw_codedate_frominclude_pinpoint

draft_mock_decision

Draft mock decision

Build a research-only mock decision outline from user facts. Combines relevant Swiss case law retrieval with statute references. If possible, enriches statutes with Fedlex text excerpts. IMPORTANT: The tool may return clarification questions (high/medium priority). High-priority clarifications must be answered (via the clarifications parameter) before the tool will provide a conclusion. Call again with clarifications to get the full analysis.

輸入參數facts必填limitquestionfedlex_urlsclarificationsdeciding_courtpreferred_languagestatute_references

get_case_brief

Case brief

Structured case brief: regeste, Sachverhalt, key Erwägungen (first 12 only — for orientation, NOT a pinpoint ranking), Dispositiv, statutes, citation authority, related cases. Accepts BGE ref, decision_id, or docket. To pinpoint which Erwägung supports a specific claim, use find_relevant_erwaegung — never guess from key_erwaegungen alone.

輸入參數case必填

get_decision_structure

Decision structure

Structured decision fields: Sachverhalt (facts), Erwägungen as numbered paragraphs ('1', '1.1', '2.3'), Dispositiv (ruling), Regeste (BGE only). Federal decisions only; for cantonal use get_decision. Returns excerpts; for verbatim full text of one Erwägung, use get_erwaegung. PINPOINT POLICY: never guess. If user gave an e_number → verify with get_erwaegung. If user gave only a claim → use find_relevant_erwaegung (FTS5+BM25 with confidence labels). If neither is confident, report no_match — do NOT default to E. 3.1.

輸入參數decision_id必填

get_erwaegung

Get Erwägung (verbatim)

Verbatim text of ONE numbered Erwägung — the citable unit in Swiss practice (e.g. 'BGE 136 III 513 E. 2.3'). Use when the user already gave an e_number. If only a claim was given (no e_number): use find_relevant_erwaegung — never guess. Returns text + sibling Erwägung numbers; a parent (E. 2.2) includes its sub-Erwägungen in order, each also under `parts`. e_number: '1', '4.1', '5.2.1', …

輸入參數e_number必填decision_id必填

find_relevant_erwaegung

Find relevant Erwägung

Find which Erwägung paragraph(s) match a legal claim. Server-side FTS5+BM25 over per-paragraph text; returns top-k with confidence labels (high/medium/low) and a highlighted_snippet wrapping the matched sentence in <mark>…</mark> (quote it verbatim). When no_match=true or confidence=low, do NOT cite any Erwägung — report no_match. Federal decisions only. Replaces the 'always-E.-3.1' guessing pattern.

輸入參數claim必填top_kdecision_id必填

get_article_purpose

Article purpose (ratio legis)

Return verbatim text from the Federal Council Botschaft (or Erläuterungsbericht) explaining the legislative purpose of a specific article. Joins the verbatim Botschaft corpus (post-2003 BBl publications, Akoma Ntoso XML where available, PDF fallback otherwise) with the article-Botschaft link table. Returns one entry per source — original Botschaft + any amendment Botschaften. All paragraphs are verbatim text the LLM can quote with `bbl_citation, S. {page}` references. USE THIS when the user asks 'what was the purpose of Art. X' or 'what did Parliament intend' or 'why does Art. X exist'. Coverage is rolling — empty `sources` means the verbatim corpus hasn't ingested that article's Botschaft yet; fall back to get_doctrine for the digest layer.

輸入參數article必填languagesr_number必填max_paragraphs

search_botschaft

Search Botschaft

Retrieve and quote the VERBATIM text of federal Materialien: Federal Council Botschaften, Berichte and Stellungnahmen des Bundesrates, parliamentary committee reports, Erlassentwürfe — and the Vernehmlassung documents: Erläuternde Berichte, Vorlagen, Ergebnisberichte, Stellungnahmen (2,500+ procedures from Fedlex). Each hit is tagged `doc_type`; filter with doc_type, default all. Bundesblatt hits come in `results` (bbl_citation, title, publication_date from Fedlex where available — `publication_date_source` says whether it is Fedlex's date or only the BBl year — page, section, article anchor); Vernehmlassung passages come in `vernehmlassung_results` (or in `results` when doc_type is a Vernehmlassung type), with the whole paragraph `text`, PDF page, procedure title, dates, office and Fedlex ELI link. Use it when starting from a concept rather than an SR/article (get_article_purpose answers 'why does Art. X exist?'). Quote verbatim. No paragraph with every term: ranked OR, flagged `filters_relaxed`.

輸入參數limitquery必填doc_typelanguageyear_maxyear_min

get_article_history

Article history

Chronological story of a single statute article: current text + every linked Botschaft (enacted + amendments) + leading court interpretations + doctrinal commentary, all ordered by date. Combines statutes.db, materialien.db, reference_graph.db, and ok_commentaries.db into one timeline. USE THIS when the user asks 'how did Art. X evolve' or 'what's the full picture on Art. X' — a single call returns the legislative + judicial + doctrinal arc.

輸入參數article必填languagesr_number必填leading_cases_limit

get_regeste

Get Regeste

Get the official Regeste (head-note) of a Swiss court decision. The Regeste is the court's own formulation of the legal rule established — for BGEs especially, this is the canonical citation target. Often references specific Erwägungen via '(E. 5.2.1)' which can then be retrieved verbatim with get_erwaegung. USE THIS when the user asks 'what does this case stand for' or 'what is the rule from this decision'. Available for ~54% of federal decisions (100% of BGE).

輸入參數decision_id必填

check_claim_support

Check claim support

Verify whether a Swiss court decision actually supports a legal claim. Uses an independent Sonnet judge to compare the claim against verbatim text from the decision (Erwägung if pinpoint given, else Regeste, else first portion of full text). Returns {supports: yes|partial|no|contradicts|unrelated, confidence, supporting_excerpt, qualifying_excerpt, reasoning}. CALL THIS for any claim where citing the wrong authority would mislead the user — especially when paraphrasing a decision or drawing a proposition from a complex Erwägung. If supports=no or contradicts, do NOT use the cited decision for that claim.

輸入參數claim必填pinpointdecision_id必填

attest_response

Attest response

MANDATORY FINAL-STEP AUDIT of your draft answer. Checks five hallucination classes: (1) every case citation (BGE/BGer/BVGer/BStGer/BPatGer/MKGE, FR/IT forms) exists and any pinpoint (E. X.Y) resolves to a real Erwägung; (2) every 'Art. X LAW' statute reference resolves; (3) quoted passages (60-400 chars) near a citation or statute reference are verbatim in that source (DE/FR/IT; audit_quotes, on by default); (4) decision dates adjacent to citations match the stored dates; (5) with audit_grounding=true, an LLM judge checks that each cited source supports the preceding claim (one call, ~3 s). Returns the annotated draft, an issues list and a `ledger` (denominator per check). It certifies the draft's own citations, quotations and statute references; it does not certify that no relevant authority is missing. CALL THIS BEFORE emitting any answer with a case citation, statute reference or quotation; set audit_grounding=true for 2+ citations. If ok=false, fix each issue; if ok=true, send linked_text verbatim.

輸入參數draft_text必填audit_quotesaudit_grounding

cite

Canonical citation

Get the canonical Swiss citation string for a decision reference. CALL THIS BEFORE writing any case citation in your response. Returns ready-to-embed citation_string (DE/FR/IT variants plus a canonical URL) and a verbatim rule_statement. If the reference doesn't exist, returns exists=false plus close_matches for typo-correction — DO NOT guess or construct citations yourself; if you get exists=false, either re-query with a close match or skip the citation entirely. Accepts any Swiss reference form: decision_id (bger_4A_747_2012), BGE reference (BGE 140 III 86), or docket number (4A_747/2012). Optional pinpoint ('2.3') generates the Erwägung-anchored citation and URL. A pinpoint the decision does not contain returns pinpoint_valid=false plus the UNPINPOINTED citation_string — never re-append the rejected pinpoint to it.

輸入參數languagepinpointreference必填

get_doctrine

Get doctrine

Get statute text + leading cases + doctrinal timeline + Federal Council Botschaft (legislative intent) + scholarly commentary for a Swiss law article or legal concept. ALWAYS USE THIS for questions about the purpose, intent, or ratio legis of a provision — it returns the Botschaft (Materialien) alongside the case law. Input: statute reference ('Art. 41 OR', 'Art. 8 BV') or legal concept. Returns: current statute text, top 5-8 BGEs ranked by citation authority with the rule each establishes, doctrine evolution timeline, Botschaft reference (legislative intent from the Federal Council's message), and scholarly commentary excerpt from OnlineKommentar.ch when available.

輸入參數query必填

generate_exam_question

Generate exam question

Generate a Swiss law exam practice question (Fallbearbeitung) based on a real BGE. Returns a fact pattern (Sachverhalt) from a real court decision and a hidden analysis (applicable statutes, leading case, legal test, correct outcome). Workflow: present the fact_pattern and hint to the student, wait for their analysis, then reveal the analysis field and compare. The student can then call get_case_brief(source_decision_id) to study the full case. Pass exclude_ids from previous calls to avoid repeating the same case.

輸入參數topic必填exclude_ids

get_law

Get law

AUTHORITATIVE LOOKUP for the text of any Swiss law article — federal OR cantonal — from two local mirrors. Federal (canton='CH', default): Fedlex mirror, all federal acts (OR, ZGB, StGB, ZPO, StPO, BV, SchKG, BGG, DBG, AIG, BVG, KVG …) in DE/FR/IT; names match in any language, case, dots or spaces (Cst, OPP2), treaties by common name (IPBPR, KRK, CISG) and former names (OG → BGG, noted; as_of gives the old edition). Cantonal: LexFind mirror, every act of all 26 cantons in the canton's language; name it 'ZH/StG' or pass canton. Use this BEFORE relying on training-data recall — statute text changes often and LLMs hallucinate article content. With as_of: the dated Fedlex edition in force on that date (snapshot_date, in-force window, link); always tell the user the edition date. PDF-only editions carry `text_status` 'heading_only' / 'empty' = no article text recovered; say so and give the Fedlex link. Examples: get_law(abbreviation='BV', article='8'); get_law(canton='ZH', sr_number='554.5', article='1').

輸入參數as_ofcantonarticlelanguagesr_numberabbreviationsystematic_number

search_laws

Search laws

Use this tool to find STATUTE ARTICLES by topic — full-text over every locally indexed article, federal (Fedlex mirror) AND cantonal (LexFind mirror, all 26 cantons), interleaved. Not for whole laws by name (search_legislation) or court decisions (search_decisions). BM25-ranked per corpus, merged by interleaving so each response surfaces both jurisdictions. Returns ranked snippets with article number, heading, law title, canton, and level ('federal' | 'cantonal'). Use this as the DEFAULT entry point when the user asks about any Swiss legal topic and you don't know which law or which jurisdiction applies. Filter with canton='ZH' (etc.) for cantonal-only, or jurisdiction='federal'/'cantonal' for explicit scoping. Examples: search_laws(query='Verjährung') — find statute-of-limitations provisions in federal + cantonal laws; search_laws(query='Hundehaltung', canton='ZH') — ZH dog-keeping rules; search_laws(query='Mietrecht', jurisdiction='federal') — federal-only tenancy law.

輸入參數limitquery必填cantonlanguagesr_numberjurisdiction

get_commentary

Get commentary

Look up a scholarly legal commentary from OnlineKommentar.ch (CC-BY-4.0) for a Swiss federal law article. Without article: lists available commentaries for that law. With article: returns the full commentary text, authors, and citation. Covers 19 Swiss laws including BV, OR, ZGB, StGB, StPO, ZPO, DSG, SchKG, and more. Cantonal: the Kommentar zur Schaffhauser Verwaltungsrechtspflege (Meyer/Herrmann/Bilger, 2021; CC BY-SA text) for the Schaffhausen VRG (SHR 172.200) and JG (SHR 173.200), article by article with margin numbers — pass canton='SH' and abbreviation 'VRG' or 'JG' (or 'SH/VRG'). Where no commentary exists (most provisions: the corpus holds only the open-access OnlineKommentar.ch / OpenLegalCommentary.ch commentaries) the answer is flagged `found: false` / `no_commentary: true` and lists what the corpus holds on the provision instead — `leading_cases`, a `doctrine` excerpt, `scholarship_citing_statute` — none of which is commentary.

輸入參數cantonarticlelanguagesr_numberabbreviation

search_commentaries

Search commentaries

Use this tool ONLY for article-anchored commentary — doctrinal discussion tied to a specific statute article: OnlineKommentar.ch / OpenLegalCommentary.ch (federal law) and the Kommentar zur Schaffhauser Verwaltungsrechtspflege (Schaffhausen VRG and JG; abbreviation 'VRG SH' / 'JG SH'). For the full OA scholarship corpus (which includes all of these) use search_scholarship. Searches commentary text, titles, and article numbers. Returns ranked results with snippets, authors, links and each hit's source. Useful for finding doctrinal discussion of a legal concept across multiple laws.

輸入參數limitquery必填languageabbreviation

search_scholarship

Search scholarship

Use this tool for open-access SCHOLARSHIP (Lehre): OA journal articles (sui generis et al.), OA legal commentaries (OnlineKommentar, OpenLegalCommentary — this tool SUBSUMES search_commentaries), dissertations and theses from Swiss university repositories, and federal legal-policy reports. Returns ranked results with snippets, authors, DOI, and direct links. Filters by source, publication type, language, and year range. Sort by relevance or year; an author filter may be used on its own (no query) for a bibliographic browse, e.g. all works by a given author, newest first.

輸入參數sortlimitqueryauthorsourcelanguagepub_typeyear_maxyear_min

get_scholarship

Get scholarship record

Fetch a single OA legal publication (article, dissertation, commentary, etc.) by its pub_id. Returns full metadata + abstract + full text if available + cross-citations to statutes and decisions.

輸入參數pub_id必填

find_scholarship_citing_statute

Scholarship citing statute

Find OA legal scholarship that cites a given Swiss statute article. Sourced from article-anchored commentaries (OnlineKommentar / OpenLegalCommentary) PLUS full-text citation extraction across the open-access journal corpus (~90k statute references resolved from 9k full-text records).

輸入參數limitarticlesr_number必填

find_scholarship_citing_decision

Scholarship citing decision

Find OA legal scholarship that cites a specific Swiss court decision. Reverse direction of the scholarship↔caselaw bridge: given a decision_id (e.g. 'bge_BGE_140_III_86' or 'bger_4A_571_2008'), return open-access publications that cite it in their full text. Citations are extracted deterministically by regex + lookup against the canonical decision corpus, so resolved citations are always to decisions we hold.

輸入參數limitdecision_id必填

list_scholarship_sources

List scholarship sources

List the open-access legal scholarship sources currently indexed, with publication counts by source, type, and language. Use this to discover what corpora are available before searching.

無需輸入參數。

get_scholarship_full_text

Scholarship full text

Fetch the full text of an OA legal publication by pub_id, on demand. Cached after first fetch so subsequent calls are instant. License-gated: only records under CC-BY-* (except CC-BY-ND), OA-Swiss-federal, or OA-author-permitted-reuse are extracted. ND/all-rights-reserved records return the upstream URL only. Use this when search_scholarship or get_scholarship returns only abstract/title and you need the article body to verify a claim or quote a passage.

輸入參數pub_id必填

get_materialien

Get Materialien

Look up Materialien for a Swiss federal law article. Returns: `sources` = per-article digests of legislative intent (BV and BGFA only); `botschaft_documents` = the Bundesblatt documents that concern the article (Botschaften, Berichte, Stellungnahmen, committee reports, Erlassentwürfe; tagged `doc_type`, with BBl citation, title, publication_date from Fedlex where available — `publication_date_source` 'bbl_year' means only the year — and Fedlex link); `vernehmlassungen` = the consultation procedures Fedlex links to the act, each with its Erläuternder Bericht, Vorlage, Ergebnisbericht and Stellungnahmen (with an article: the Erläuternder-Bericht passages on it); `amendment_refs` = the AS/BBl locators from the statute's footnotes. Empty `sources` means no digest, NOT that the law has no legislative history. Full text: search_botschaft. Nothing keyed to a provision: flagged `found: false` and lists `leading_cases`, a `doctrine` excerpt and `scholarship_citing_statute` instead — none of it legislative history.

輸入參數articledoc_typelaw_code必填

search_materialien

Search Materialien

Use this tool for DIGESTED legislative history — per-article intent summaries and key arguments, not verbatim text. COVERAGE IS NARROW: BV and BGFA digests (167 articles) plus BV debate transcripts, and nothing else. For any other law this returns 0 hits, which means 'outside this index', not 'no materials exist'. Each hit names its source message (bbl_ref, title, publication_date from Fedlex where available, with `publication_date_source`). Prefer search_botschaft, which covers all 10,100+ Federal Council messages plus Berichte, Stellungnahmen, committee reports, Erlassentwürfe and the Vernehmlassung documents (Erläuternde Berichte, Ergebnisberichte) verbatim (filter by doc_type), or get_materialien(law_code) for the documents linked to one statute.

輸入參數limitquery必填doc_typelaw_code

search_legislation

Search legislation

Use this tool to find WHOLE LAWS by name or subject — 33,000+ federal and cantonal enactments from LexFind.ch; the entry point for cantonal questions ('Hundegesetz Bern', 'loi sur l'énergie Vaud'). Not for article-level topic search (search_laws) or the core federal codes (get_law is instant there). All 26 cantons + federal, DE/FR/IT. Also the entry point for municipal regulations and federal ordinances outside the core Fedlex mirror. SINGLE-CALL MODE: set fetch_top_n_texts=1..3 and the top results are returned with the parsed full text + article list of the law itself — no follow-up get_legislation call needed. Ideal for 'what does cantonal law X say about Y' questions. For core federal codes (OR, ZGB, StGB, BV, StPO, ZPO, SchKG, BGG, DBG, IPRG, AIG, BVG, KVG etc.), prefer get_law / search_laws — they are instant and cover all three languages.

輸入參數limitquery必填cantonlanguageactive_onlyfetch_top_n_textssearch_in_content

get_legislation

Get legislation

Retrieve the FULL TEXT and article list of a specific Swiss law, federal or cantonal, by LexFind ID or SR/systematic number. For federal laws in the Fedlex mirror this is instant (local SQLite). For cantonal laws, the law is downloaded from LexFind as PDF, parsed with PyMuPDF, and segmented into articles (cached 30 days). Returns: title, entity, articles (article_num, heading, text), full_text, article_count. Use search_legislation first to find the right lexfind_id or systematic_number; then pass it here. For the core federal codes, get_law is still the fastest path.

輸入參數cantonlanguagelexfind_idinclude_versionssystematic_number

browse_legislation_changes

Browse legislation changes

Browse recent legislation changes for a canton or federal level. Shows new laws, amendments, and abrogations with dates.

輸入參數cantonlanguage

search_practice

Search administrative practice

Use this tool when the question involves federal ADMINISTRATIVE PRACTICE (Verwaltungspraxis): Wegleitungen, Kreisschreiben, Weisungen, Rundschreiben — agency guidance, not court decisions. 9,900+ documents. Covered: BSV (AHV/IV/EL/EO/FamZ/BVG Wegleitungen, Kreisschreiben, Mitteilungen, every version), OAK BV (BVG Weisungen, Mitteilungen, drafts), WEKO (Bekanntmachungen, Erläuterungen, all versions), SECO (ArG commentary, AVIG-Praxis), BAG (KVG Kreisschreiben), SEM (Weisungen AIG/Asyl/BüG, Handbuch Asyl), BJ (SchKG Weisungen, Existenzminimum), FINMA, ESTV, BAFU; counts in the filter enums. Returns ranked excerpts with authority, number, date, PDF link. Superseded versions collapse to the newest per document; for past conduct pass include_superseded=true and take the version dated before the facts. NOT covered: cantonal administrations — Sozialhilfe (SKOS, cantonal Handbücher), Prämienverbilligung (IPV) — say so, do not imply absence of guidance. Pre-2017 federal decisions: search_decisions(court='ch_vb').

輸入參數limitquery必填sourcedoc_typelanguageissuing_authorityinclude_superseded

get_practice

Get administrative practice

Retrieve a single federal administrative-practice document by its doc_id (e.g. 'estv_ks_ks_nr_28', 'sem_weisungen_weisungen-aug-d', 'bsv_weisungen_6930_v20_de', 'seco_alv_avig_ale_de'). Returns full body text, title, date, issuing authority, and PDF URL. Use search_practice first to discover the doc_id.

輸入參數doc_id必填

版本歷史

1
  1. v1.3.0最新Sep 16, 2026

評分

已滿足適用檢查項的 100%

該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。

  • 近 30 天內可連通必需25 分
  • 已宣告認證方式必需10 分
  • 已列出工具必需15 分
  • 描述必需15 分
  • 圖示10 分
  • 文件15 分
  • 授權條款10 分