Fail-closed case-law citation verification for legal agents and drafting workflows.
Project description
CaseLaw Guard
CaseLaw Guard is an Apache-2.0 verifier for agents and drafting workflows that need to fail closed on fabricated case-law citations.
The v0 guarantee is deliberately narrow: citation existence only. It does not decide whether a case supports a legal proposition, whether a case remains good law, or whether any output is legal advice.
What It Does
- Extracts case-law citations locally from plain text or Markdown.
- Verifies citations through configured legal-source adapters.
- Sends only citation strings or citation components to external providers by default.
- Returns stable JSON that agents can use to block, retry, or show unresolved citations.
- Exits non-zero from the CLI when any extracted citation is not verified.
Install
For the command-line verifier:
python3 -m pip install caselaw-guard
For local agent or MCP use:
python3 -m pip install "caselaw-guard[mcp]"
Quickstart
Verify a draft after setting up a provider:
caselaw-guard verify draft.md
CaseLaw Guard fails closed. If no configured provider supports an extracted citation, the result is not treated as verified.
Most users need one provider setup:
| Citation source | Setup |
|---|---|
| U.S. case citations | Set CASELAW_GUARD_COURTLISTENER_TOKEN or pass --courtlistener-token. |
| Australian neutral citations | Pass --au-index /path/to/australia-index.json or set CASELAW_GUARD_AU_INDEX. |
| Local agents over MCP | Install caselaw-guard[mcp], then run caselaw-guard-mcp. |
For a no-network smoke test, verify Australian neutral citations from stdin using a local index:
printf 'Mabo v Queensland (No 2) [1992] HCA 23\n' \
| caselaw-guard verify - --no-courtlistener --au-index examples/australia_index.sample.json
The CLI exits 0 only when every extracted citation is verified; otherwise it exits 1.
CLI
Verify a Markdown or text draft with explicit providers:
caselaw-guard verify draft.md \
--courtlistener-token "$CASELAW_GUARD_COURTLISTENER_TOKEN" \
--au-index examples/australia_index.sample.json
Use an opt-in CourtListener cache when repeated checks are expected:
caselaw-guard verify draft.md \
--courtlistener-token "$CASELAW_GUARD_COURTLISTENER_TOKEN" \
--cache .cache/courtlistener.json \
--cache-ttl-days 30
The cache stores citation lookup inputs and provider results only. It does not store source document text, and provider errors or rate-limit responses are not cached.
Build a compact Australian citation index from a local Open Australian Legal Corpus corpus.jsonl:
caselaw-guard au-index build ~/Downloads/corpus.jsonl \
--output data/australia-index.json
The builder only indexes rows where type == "decision", extracts neutral citations from citation, and omits the full text field.
REST API
Run the API:
uvicorn caselaw_guard.api:app --reload
Request:
curl -X POST http://127.0.0.1:8000/verify \
-H 'content-type: application/json' \
-d '{"text":"Obergefell v. Hodges, 576 U.S. 644"}'
Response shape:
{
"pass": false,
"results": [
{
"citation": "576 U.S. 644",
"start_index": 22,
"end_index": 34,
"jurisdiction_guess": "us",
"provider": null,
"normalized_citation": "576 U.S. 644",
"authority": null,
"source_url": null,
"status": "unsupported_format",
"confidence": 0.0,
"error_message": "No configured adapter supports this citation format.",
"candidates": [],
"provider_metadata": {}
}
]
}
MCP Server
Run the local stdio MCP server directly to confirm it starts:
caselaw-guard-mcp
The server exposes one tool, verify_case_law_text, which accepts text and returns the same JSON report shape as the CLI and REST API.
For agent configuration, prefer an absolute path to the installed script so the agent does not depend on shell startup files or PATH inheritance. In a local checkout, that path is typically:
/path/to/caselaw-guard/.venv/bin/caselaw-guard-mcp
Codex
Add the server to ~/.codex/config.toml:
[mcp_servers.caselaw-guard]
command = "/path/to/caselaw-guard/.venv/bin/caselaw-guard-mcp"
[mcp_servers.caselaw-guard.env]
CASELAW_GUARD_COURTLISTENER_TOKEN = "your-courtlistener-token"
CASELAW_GUARD_AU_INDEX = "/absolute/path/to/australia-index.json"
CASELAW_GUARD_CACHE = "/absolute/path/to/courtlistener-cache.json"
CASELAW_GUARD_CACHE_TTL_DAYS = "30"
Claude Code
Register the same stdio server with claude mcp add-json:
{
"type": "stdio",
"command": "/path/to/caselaw-guard/.venv/bin/caselaw-guard-mcp",
"env": {
"CASELAW_GUARD_COURTLISTENER_TOKEN": "your-courtlistener-token",
"CASELAW_GUARD_AU_INDEX": "/absolute/path/to/australia-index.json",
"CASELAW_GUARD_CACHE": "/absolute/path/to/courtlistener-cache.json",
"CASELAW_GUARD_CACHE_TTL_DAYS": "30"
}
}
claude mcp add-json caselaw-guard '{"type":"stdio","command":"/path/to/caselaw-guard/.venv/bin/caselaw-guard-mcp","env":{"CASELAW_GUARD_COURTLISTENER_TOKEN":"your-courtlistener-token","CASELAW_GUARD_AU_INDEX":"/absolute/path/to/australia-index.json","CASELAW_GUARD_CACHE":"/absolute/path/to/courtlistener-cache.json","CASELAW_GUARD_CACHE_TTL_DAYS":"30"}}'
MCP Environment
Set only the provider environment variables you need:
| Variable | Required | Purpose |
|---|---|---|
CASELAW_GUARD_COURTLISTENER_TOKEN |
Required for U.S. citation lookup | Enables the CourtListener adapter. |
CASELAW_GUARD_AU_INDEX |
Required for Australian citation lookup | Points to a compact Australian citation index JSON file. |
CASELAW_GUARD_CACHE |
Optional | Enables the CourtListener lookup cache. |
CASELAW_GUARD_CACHE_TTL_DAYS |
Optional | Overrides the default CourtListener cache TTL of 30 days. |
Omit provider environment variables for adapters you do not want to enable.
Troubleshooting
- Use an absolute
commandpath if the agent cannot findcaselaw-guard-mcp. - Verify installation with
/path/to/caselaw-guard/.venv/bin/caselaw-guard-mcp; stop it withCtrl+Cafter it starts. - Confirm provider environment variables are present in the agent MCP config, not only in your interactive shell.
- If the server exits with an MCP install hint, reinstall with
python3 -m pip install -e ".[mcp]".
Adapters
CourtListener
The CourtListener adapter verifies U.S. citations through the CourtListener citation lookup API. Configure it with CASELAW_GUARD_COURTLISTENER_TOKEN or --courtlistener-token.
The adapter sends citation components such as volume=576, reporter=U.S., and page=644, not the full source document.
Set CASELAW_GUARD_CACHE to enable a persistent cache without passing --cache; set CASELAW_GUARD_CACHE_TTL_DAYS to change expiry.
Australia
The Australian adapter verifies neutral citations against a local JSON metadata index derived from sources such as the Open Australian Legal Corpus. A record should include a neutral citation and whatever authority metadata is available:
[
{
"citation": "Mabo v Queensland (No 2) [1992] HCA 23",
"normalized_citation": "[1992] HCA 23",
"case_name": "Mabo v Queensland (No 2)",
"court": "High Court of Australia",
"date": "1992-06-03",
"source_url": "https://eresources.hcourt.gov.au/showCase/1992/HCA/23"
}
]
If more than one index row has the same normalized_citation, the adapter returns ambiguous and exposes each match in candidates.
Development
python3 -m venv .venv
.venv/bin/python -m pip install ".[dev]"
.venv/bin/python -m pytest
Install MCP support from a local checkout:
.venv/bin/python -m pip install -e ".[mcp]"
Benchmarks
Run the AusLaw Citation Benchmark extraction eval manually to measure Australian neutral citation coverage:
.venv/bin/python scripts/eval_auslaw_benchmark.py \
--output .cache/caselaw-guard/auslaw-citation-benchmark/report.json
By default, the script downloads the benchmark test split from Hugging Face into .cache/caselaw-guard/auslaw-citation-benchmark/roc_test.json and reuses it on later runs. Pass --refresh to redownload it, or --input /path/to/roc_test.json to evaluate a local copy.
To measure end-to-end verification coverage against a compact Australian index, pass --au-index:
.venv/bin/python scripts/eval_auslaw_benchmark.py \
--au-index /absolute/path/to/australia-index.json \
--output .cache/caselaw-guard/auslaw-citation-benchmark/verification-report.json
Without --au-index, the eval measures extraction only. With --au-index, it also reports verification status counts and capped not-found or ambiguous examples.
Release Readiness
The package build and manual PyPI publishing workflow are validated in CI. See RELEASE.md for the release checklist.
Non-Goals For v0
- No proposition-support checking.
- No good-law or precedential-status analysis.
- No PDF or DOCX parsing.
- No legal advice.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file caselaw_guard-0.1.2.tar.gz.
File metadata
- Download URL: caselaw_guard-0.1.2.tar.gz
- Upload date:
- Size: 27.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c99698a6c3f019cbdab3ab96a08e5fb9f846e1f2a56d0337182511fb5a0d476a
|
|
| MD5 |
b9ef764ae5dc2a461e384b6cabfc4e6d
|
|
| BLAKE2b-256 |
554db695e00973c8da9256dd3e098c8351085c9c98a8d339a6a87bf5df4b4046
|
Provenance
The following attestation bundles were made for caselaw_guard-0.1.2.tar.gz:
Publisher:
publish.yml on l0cka/caselaw-guard
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
caselaw_guard-0.1.2.tar.gz -
Subject digest:
c99698a6c3f019cbdab3ab96a08e5fb9f846e1f2a56d0337182511fb5a0d476a - Sigstore transparency entry: 1417694418
- Sigstore integration time:
-
Permalink:
l0cka/caselaw-guard@956a02dd5fbe3177722008d039c14d70058d1a8b -
Branch / Tag:
refs/heads/main - Owner: https://github.com/l0cka
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@956a02dd5fbe3177722008d039c14d70058d1a8b -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file caselaw_guard-0.1.2-py3-none-any.whl.
File metadata
- Download URL: caselaw_guard-0.1.2-py3-none-any.whl
- Upload date:
- Size: 21.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
68d7658d126d466c4a694dfc77e79140f5076c406172b866e950b7542e4b3eeb
|
|
| MD5 |
80a1e61503f404fd89b95b17b58b6093
|
|
| BLAKE2b-256 |
2e80e109c8a709ab9ee8568b8241f7a08e04413bb9fbb731d99a0c533c13531d
|
Provenance
The following attestation bundles were made for caselaw_guard-0.1.2-py3-none-any.whl:
Publisher:
publish.yml on l0cka/caselaw-guard
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
caselaw_guard-0.1.2-py3-none-any.whl -
Subject digest:
68d7658d126d466c4a694dfc77e79140f5076c406172b866e950b7542e4b3eeb - Sigstore transparency entry: 1417694420
- Sigstore integration time:
-
Permalink:
l0cka/caselaw-guard@956a02dd5fbe3177722008d039c14d70058d1a8b -
Branch / Tag:
refs/heads/main - Owner: https://github.com/l0cka
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@956a02dd5fbe3177722008d039c14d70058d1a8b -
Trigger Event:
workflow_dispatch
-
Statement type: