mcp-courtwatch
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@mcp-courtwatchsearch for tenant rights opinions in California"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
mcp-courtwatch
MCP server for free U.S. case-law and court-docket search, over CourtListener (the Free Law Project's open legal database). Built for legal-aid orgs, tenant-defense and pro-se litigants, and public-interest lawyers who cannot afford Westlaw or PACER.
It wraps CourtListener's REST API v4, normalizing the raw JSON (caseName, dateFiled, cluster_id, docket_absolute_url, and so on) into clean, documented tool outputs.
Tools
Tool | Arguments | Returns |
|
| Full-text case-law search ( |
|
| Docket search ( |
|
| Courts and their ids ( |
|
| Full case by id. A cluster ( |
|
| Verify citations ( |
|
| Judges / people ( |
order_by for opinion_search is one of relevance (default), newest, oldest, most_cited. Court jurisdiction codes include F (federal appellate and other), FD (federal district), FB (bankruptcy), S (state), SA (state appellate), SS (state supreme).
Related MCP server: courtlistener-mcp
Data source
Base URL:
https://www.courtlistener.com/api/rest/v4Auth: a free API token, sent as the header
Authorization: Token <token>.Envelope: search and list endpoints return the DRF shape
{ count, next, previous, results: [...] }./search/paginates by opaquecursor; the list endpoints (/courts/,/people/) paginate by page number (?page=N). Detail endpoints return a bare object.POST /citation-lookup/returns a bare JSON array (one item per citation recognized in the text).Access:
/search/,/courts/, and/people/answer without a token at a low rate limit, so those tools attach the token only when it is set (a token raises the limit)./clusters/{id}/,/opinions/{id}/, andPOST /citation-lookup/return HTTP 401 without a token, socase_detailandcitation_lookuprequire one.Citation-lookup limits (server-side):
textmax 64,000 characters (enforced pre-flight here with a clear error; the tool never truncates, since a dropped tail would mean unchecked citations); the first 250 citations per call are looked up and any beyond that come back flagged per-item as not checked; rate limit 60 citations/min.
Sources:
REST API v4 overview and auth: https://www.courtlistener.com/help/api/rest/ (redirects to https://wiki.free.law/c/courtlistener/help/api/rest/v4/overview)
Search API (params,
typeenum, response fields): https://www.courtlistener.com/help/api/rest/search/ (redirects to https://wiki.free.law/c/courtlistener/help/api/rest/v4/search)Case Law API (clusters, opinions): https://www.courtlistener.com/help/api/rest/case-law/ (redirects to https://wiki.free.law/c/courtlistener/help/api/rest/v4/case-law)
Citation Lookup API: https://www.courtlistener.com/help/api/rest/citation-lookup/ (redirects to https://wiki.free.law/c/courtlistener/help/api/rest/v4/citation-lookup), plus the endpoint's source in the CourtListener repo:
cl/citations/api_views.pyandapi_serializers.py(request/response fields, per-citation status codes) andcl/settings/project/citations.py(the 250-citations-per-request cap). The endpoint returns HTTP 401 without a token.The live API itself, for the search / courts / people field names:
/search/?type=o,/search/?type=r,/courts/,/people/(all answer unauthenticated GETs).
Field map (CourtListener to normalized output)
CourtListener field | Normalized field | Where |
|
| opinion_search, docket_lookup |
|
| search hits |
|
| search hits |
|
| opinion_search |
|
| search hits |
|
| opinion_search |
|
| search hits |
|
| search hits |
|
| docket_lookup |
|
| court_list |
|
| case_detail (cluster) |
|
| case_detail (cluster) |
|
| case_detail (opinion) |
| same names | citation_lookup (per citation) |
|
| citation_lookup (per citation) |
|
| citation_lookup (per citation) |
Install
No build step. Runs directly on tsx.
git clone https://github.com/haksanlulz/mcp-courtwatch.git
cd mcp-courtwatch
npm installAPI token
case_detail and citation_lookup need a free CourtListener token, and the other tools run faster (higher rate limit) with one. Create a free account, open Profile then the API page, and copy the token. Docs: https://www.courtlistener.com/help/api/rest/
Expose it as COURTLISTENER_API_TOKEN:
export COURTLISTENER_API_TOKEN=your-token-here # macOS / Linux
setx COURTLISTENER_API_TOKEN your-token-here # Windows (new shells)Without the token, opinion_search, docket_lookup, court_list, and judge_lookup still work at CourtListener's unauthenticated rate limit. case_detail and citation_lookup return a clear error telling you to set the token. The token is never logged.
MCP client config
Point your MCP client at index.ts via tsx. Use an absolute path.
{
"mcpServers": {
"courtwatch": {
"command": "npx",
"args": ["tsx", "/absolute/path/to/mcp-courtwatch/index.ts"],
"env": { "COURTLISTENER_API_TOKEN": "your-token-here" }
}
}
}Example
Call opinion_search with { "q": "warrantless search", "court": "scotus", "order_by": "most_cited", "limit": 1 }:
{
"query": { "q": "warrantless search", "court": "scotus", "filed_after": null, "filed_before": null, "order_by": "most_cited" },
"total_matches": 161,
"returned": 1,
"results": [
{
"case_name": "Illinois v. Gates",
"court": "Supreme Court of the United States",
"court_id": "scotus",
"date_filed": "1983-06-08",
"citations": ["76 L. Ed. 2d 527", "103 S. Ct. 2317", "462 U.S. 213", "1983 U.S. LEXIS 54", "51 U.S.L.W. 4709"],
"docket_number": "81-430",
"cite_count": 16741,
"status": "Published",
"snippet": "Justice White, concurring in the judgment. In my view, the question regarding modification of the exclusionary rule ...",
"cluster_id": 110959,
"docket_id": 638808,
"absolute_url": "https://www.courtlistener.com/opinion/110959/illinois-v-gates/"
}
]
}Then pass the cluster_id to case_detail ({ "id": 110959 }) for the citations, judges, and opinion ids, or case_detail with { "id": <opinion id>, "type": "opinion" } for the full opinion text.
Example: verifying citations before filing
Courts have sanctioned filings built on citations that do not exist. A legal-aid worker or a pro-se litigant can run a draft's citations through citation_lookup before anything reaches a judge:
Call citation_lookup with { "text": "Tenants are protected here. See Roe v. Wade, 410 U.S. 113 (1973); Smith v. Imaginary, 999 U.S. 9999 (2099)." }:
{
"query": { "text_chars": 107 },
"citations_checked": 2,
"found": 1,
"not_found": 1,
"invalid": 0,
"not_checked": 0,
"all_verified": false,
"warning": "1 of 2 citation(s) did NOT verify: 1 not found in CourtListener (likely fabricated or mis-cited). Do not cite unverified authorities — check them by hand before filing.",
"results": [
{
"citation": "410 U.S. 113",
"verified": true,
"verdict": "FOUND",
"status": 200,
"error_message": null,
"normalized_citations": ["410 U.S. 113"],
"start_index": 45,
"end_index": 57,
"matches": [
{
"cluster_id": 108713,
"case_name": "Roe v. Wade",
"date_filed": "1973-01-22",
"citations": ["410 U.S. 113", "93 S. Ct. 705", "35 L. Ed. 2d 147"],
"precedential_status": "Published",
"citation_count": 12030,
"judges": "Blackmun",
"docket_id": 4463,
"absolute_url": "https://www.courtlistener.com/opinion/108713/roe-v-wade/"
}
]
},
{
"citation": "999 U.S. 9999",
"verified": false,
"verdict": "NOT_FOUND",
"status": 404,
"error_message": "Citation not found: '999 U.S. 9999'",
"normalized_citations": ["999 U.S. 9999"],
"start_index": 86,
"end_index": 99,
"matches": []
}
]
}The fabricated citation comes back NOT_FOUND with a top-level warning. Per-citation status mirrors the API's own codes: 200 found, 300 found with multiple matching clusters (FOUND_MULTIPLE — a real citation, ambiguous mapping), 400 unknown reporter, 404 not found, 429 past the 250-citations-per-call cap (NOT_CHECKED_OVER_CAP — split the text and re-run the rest). A lookup that recognizes zero citations says so in a note instead of pretending to have verified anything.
Caveats
Built without a token, so:
The
case_detailfield names (cluster and opinion objects) come from the Case Law API docs plus the search-result shape, not from a live authenticated GET (the/clusters/and/opinions/endpoints return HTTP 401 without a token). The normalizer is defensive: unknown or missing values coerce tonull(or empty arrays) rather than throwing, and citations accept either string or object form. Runnpm run smokewith a real token to confirm these end to end.The
citation_lookupresponse shape (per-citation fields, the 200/300/400/404/429 status codes, the serializer-level 64,000-char text cap, the 250-citation per-request cap) comes from the CourtListener source (cl/citations/api_views.py,api_serializers.py,cl/settings/project/citations.py) and the Citation Lookup docs, not from a live authenticated POST (the endpoint returns HTTP 401 without a token). The normalizer is defensive like the rest. Runnpm run smokewith a token: it checks one real citation (410 U.S. 113, Roe v. Wade) plus one fabricated one and fails unless the real one resolves and the fake comes backNOT_FOUND.The clusters returned by
citation_lookupdo not include the court (in CourtListener's model the court hangs off the docket, not the cluster). For the court, follow the match'sabsolute_urlor pass itscluster_idtocase_detailand thedocket_idonward. Deliberately not auto-fetched here: a 250-citation brief would fan out into hundreds of extra docket calls.docket_numberis folded into the free-textqterm rather than sent as a dedicated field, so matching is best-effort. If a known docket number under-returns, also pass the case name inq.court_listapplies itsqname filter across the full courts table, walked one page at a time server-side (the/courts/endpoint ignorespage_size, so the ~3,400 courts span ~170 pages of ~20 up to a safety cap). Scope byjurisdictionto page less; a token is recommended for the unfiltered full-table scan.opinion_searchanddocket_lookupreturn one fixed/search/page of ~20 results; for more, pass the response'snext_cursorback as thecursorargument. The endpoint ignorespage_size, solimitcannot exceed one page (it is capped at 20 rather than advertising an unreachable number).The
order_byvalues, thecourt/filed_after/filed_beforefilters, and the search / courts / people field names match the live API, as does the fixed ~20-row page size of/search/and/courts/(both ignorepage_size). The token-gated behavior ofcase_detailis the main thing the keyed smoke should confirm.
Develop
npm test # vitest, fetch mocked with the documented response shapes (no token needed)
npm run smoke # one live call per tool (needs COURTLISTENER_API_TOKEN; skips cleanly without)
npm run typecheckLicense
MIT. See LICENSE. Data from CourtListener / the Free Law Project (public court records and openly licensed legal data). Unofficial, not affiliated with CourtListener or the Free Law Project.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/haksanlulz/mcp-courtwatch'
If you have feedback or need assistance with the MCP directory API, please join our Discord server