@cyanheads/courtlistener-mcp-server
Search and retrieve US court opinions, federal dockets, judge records, citation networks, and oral arguments from CourtListener's 9M+ opinion corpus via MCP. STDIO or Streamable HTTP.
Public Hosted Server: https://courtlistener.caseyjhand.com/mcp
Tools
13 tools spanning the full CourtListener dataset — opinion search and retrieval, citation network traversal, federal docket lookup, party and attorney lookup, judge biography, judicial financial disclosures, court discovery, and oral argument search and detail:
| Tool | Description |
|---|---|
courtlistener_search_opinions | Full-text search across 9M+ written court opinions with field-level filtering, date ranges, status, and sort |
courtlistener_get_opinion | Fetch full text and metadata for an opinion cluster — returns all opinion variants (majority, concurrence, dissent) |
courtlistener_get_citations | Retrieve the citation network for an opinion: opinions cited by it (citing) or that cite it (cited_by) |
courtlistener_lookup_citation | Resolve a legal citation string (e.g., "410 U.S. 113") to a cluster ID and case metadata |
courtlistener_search_dockets | Search RECAP federal court dockets by party name, attorney, court, and date |
courtlistener_get_docket | Fetch docket metadata and entry list for a single federal case |
courtlistener_get_parties | Fetch all parties and attorneys of record for a RECAP federal docket by docket ID |
courtlistener_search_judges | Search judge records by name, appointing president, court, and political affiliation |
courtlistener_get_judge | Fetch full biographical profile, appointment history, and education for a single judge |
courtlistener_lookup_courts | List courts filtered by jurisdiction type and active-scraper status |
courtlistener_search_oral_arguments | Search appellate oral argument audio recordings by case name, court, and date argued |
courtlistener_get_oral_argument | Fetch full detail for a single oral argument — panel, duration, MP3 link, and speech-to-text transcript |
courtlistener_search_financial_disclosures | Search federal judicial financial disclosure filings by judge and year — category counts, itemized gifts, and source PDF |
courtlistener_search_opinions
Search the 9M+ opinion corpus. Returns opinion cluster summaries with matched text excerpts.
- Free-text queries with field syntax:
caseName:,court_id:,judge:,docketNumber:,cites:(id), booleanAND / OR / NOT - Filter by court ID, date range, publication status (Published / Unpublished / In-chambers, etc.)
- Sort by relevance score, filing date (asc/desc), or citation count
- Cursor-based pagination; up to 20 results per call
- Results include
cluster_id(forcourtlistener_get_opinion) anddocket_id(forcourtlistener_get_docket) for chaining
courtlistener_get_opinion
Fetch full text and metadata for an opinion cluster.
- A cluster groups all opinions filed in a case: majority, concurrence, dissent, per curiam
- Returns
html_textandplain_textfor each opinion variant; surfacesdownload_urlwhen local text is absent - Includes
cites[](outbound citation IDs),cite_count, syllabus, posture, and docket link - Single upstream request — safe within the tight free-tier rate limit
courtlistener_get_citations
Retrieve the citation network for an opinion cluster in either direction.
cited_by(default): opinions that cite this one — measures precedential influence and downstream adoptionciting: opinions this one cites — reveals the authority chain the court relied on- Optional court and date filters; cursor-based pagination; up to 20 results per call
- Results include
snippetshowing the excerpt around the citation reference - Rate-limit note: the free tier (125 req/day) supports 1–2 hops on a single case; deep multi-hop traversal exhausts the daily budget quickly
courtlistener_lookup_citation
Resolve a formatted legal citation string to a cluster ID.
- Accepts standard reporter formats: "410 U.S. 113", "347 U.S. 483", "93 S. Ct. 705"
- Returns cluster ID, case name, court, date filed, all known citation strings, and the canonical form CourtListener uses
- Single upstream POST to
/citation-lookup/; falls back to search when unauthenticated
courtlistener_search_dockets
Search RECAP federal court dockets.
- Query matched against case name, docket number, party names, and attorney names
party_namefilter applies in addition to (AND with) theqquery — more precise than embedding party names in the query- Returns up to 3 sample document entries per docket with
is_availablestatus coverage_notein every response — RECAP is crowd-sourced from PACER; completeness varies by court
courtlistener_get_docket
Fetch full docket metadata and entry list for a single federal case.
- Returns all available docket entries with document availability, page count, and RECAP file path
entries_page_sizecontrols how many entries are returned (1–50); large cases have hundreds- Documents with
is_available: falserequire a PACER account or CourtListener RECAP filing — document retrieval is not exposed
courtlistener_get_parties
Fetch all parties and attorneys of record for a RECAP federal docket.
- Returns each party's name, docket-scoped role (Plaintiff, Defendant, Petitioner, Respondent, etc.), and attorneys with contact information
- Attorney names and contact details are resolved in a single batch call per page — 2 upstream requests total per invocation
- Paginate large party lists with
pageandpage_size(max 10); keeppage_sizelow to stay within the free-tier rate limit - Obtain docket IDs from
courtlistener_search_docketsorcourtlistener_get_docket
courtlistener_search_judges
Search judge and person records across the federal and state bench.
- Filter by appointing president's last name, court ID, or political affiliation (
d/r/i/l/g/u) - Returns
person_idfor chaining tocourtlistener_get_judge, plus current position summary - Court IDs from
courtlistener_lookup_courtscan be passed directly
courtlistener_get_judge
Fetch a judge's full biographical profile.
- Complete appointment history: all courts served, position type, appointer, nomination date, confirmation date, termination reason
- Education records with school, degree, and year
- Political affiliations with date ranges; ABA ratings; Federal Judicial Center ID for cross-referencing
courtlistener_lookup_courts
List courts with optional jurisdiction and scraper filters.
- Jurisdiction codes cover federal appellate (
F), district (FD), bankruptcy (FB), state supreme (SS), state appellate (SA), tribal, and more in_use: true(default) restricts to courts currently scraped by CourtListenerhas_opinion_scraperfilter useful for planning opinion searches — courts without scrapers have sparse coverage- Returns
id(thecourt_idstring for use in all search and filter parameters), citation string (e.g., "9th Cir."), and jurisdiction label
courtlistener_search_oral_arguments
Search appellate oral argument audio recordings — the largest public collection of oral argument audio.
- Query matched against case name and transcribed argument text (where available)
- Filters by court, argued-after, and argued-before date
- Returns
download_url(MP3),duration_seconds,panel_ids(chaining tocourtlistener_get_judge), and transcriptsnippet
courtlistener_get_oral_argument
Fetch the full detail record for a single oral argument by audio ID.
- Returns the speech-to-text
transcriptwhen transcription has completed, pluspanel_ids,duration_seconds, MP3download_url, and the linkeddocket_id - Audio IDs come from
courtlistener_search_oral_argumentsresults - The argument date is not on this record — take it from the search result or the linked docket
courtlistener_search_financial_disclosures
Search federal judicial financial disclosure filings for ethics and recusal research.
- Filter by
judge_id(aperson_idfromcourtlistener_search_judges) and/or filingyear - Returns per-filing category counts (investments, gifts, debts, positions, reimbursements, income), itemized gifts, and a link to the source PDF
- Line-item investments — often hundreds per filing, with coded values — are summarized as counts; the linked PDF carries the full itemization
Features
Built on @cyanheads/mcp-ts-core:
- Declarative tool definitions — single file per tool, framework handles registration and validation
- Unified error handling across all tools
- Pluggable auth (
none,jwt,oauth) - Swappable storage backends:
in-memory,filesystem,Supabase,Cloudflare KV/R2/D1 - Structured logging with optional OpenTelemetry tracing
- STDIO and Streamable HTTP transports
CourtListener-specific:
- Complete CourtListener REST API v4 integration — opinions, dockets, judges, courts, oral arguments, citation network
- Rate-limit-aware client: 429 responses classified by window (minute / hour / day) with actionable error messages; retry with Retry-After respect
- Cursor-based pagination throughout — consistent results across all paginated endpoints
- RECAP coverage note surfaced on every docket response — sets expectations on partial PACER mirror completeness
- Tight upstream-call budget — most tools make 1–2 calls; opinion detail and citation traversal make up to 3 (resolving the linked docket or source cluster), keeping the free tier (125 req/day) usable for multi-step research
Agent-friendly output:
- Chaining IDs on every response —
cluster_id,docket_id, andperson_idfields are present wherever they enable a logical follow-up call, with field-level descriptions naming which tool to pass them to - Discriminated rate-limit errors — minute / hour / day throttle identified in structured error so agents can reason about retry timing, not just "try again later"
- Coverage caveats inline — RECAP
coverage_noteand oral argument transcriptsnippetavailability explicitly signaled so agents can communicate limitations to users rather than silently omitting them
Getting started
Public Hosted Instance
A public instance is available at https://courtlistener.caseyjhand.com/mcp — no installation required. Point any MCP client at it via Streamable HTTP:
{
"mcpServers": {
"courtlistener-mcp-server": {
"type": "streamable-http",
"url": "https://courtlistener.caseyjhand.com/mcp"
}
}
}
Self-Hosted / Local
Add the following to your MCP client configuration file. See CourtListener account settings to generate a free API token.
{
"mcpServers": {
"courtlistener-mcp-server": {
"type": "stdio",
"command": "bunx",
"args": ["@cyanheads/courtlistener-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info",
"COURTLISTENER_API_TOKEN": "your-api-token"
}
}
}
}
Or with npx (no Bun required):
{
"mcpServers": {
"courtlistener-mcp-server": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@cyanheads/courtlistener-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info",
"COURTLISTENER_API_TOKEN": "your-api-token"
}
}
}
}
Or with Docker:
{
"mcpServers": {
"courtlistener-mcp-server": {
"type": "stdio",
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "MCP_TRANSPORT_TYPE=stdio",
"-e", "COURTLISTENER_API_TOKEN=your-api-token",
"ghcr.io/cyanheads/courtlistener-mcp-server:latest"
]
}
}
}
For Streamable HTTP, set the transport and start the server:
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 COURTLISTENER_API_TOKEN=... bun run start:http
# Server listens at http://localhost:3010/mcp
Prerequisites
- Bun v1.3.2 or higher (or Node.js v24+).
- A CourtListener API token — free account at courtlistener.com. Free tier: 5 req/min, 50 req/hr, 125 req/day. Free Law Project membership unlocks higher limits.
Installation
- Clone the repository:
git clone https://github.com/cyanheads/courtlistener-mcp-server.git
- Navigate into the directory:
cd courtlistener-mcp-server
- Install dependencies:
bun install
- Configure environment:
cp .env.example .env
# edit .env and set COURTLISTENER_API_TOKEN
Configuration
All configuration is validated at startup via Zod schemas in src/config/server-config.ts. Key environment variables:
| Variable | Description | Default |
|---|---|---|
COURTLISTENER_API_TOKEN | Required. API token from your CourtListener account settings. Free tier: 5 req/min, 50/hr, 125/day. | — |
COURTLISTENER_BASE_URL | API base URL override. | https://www.courtlistener.com/api/rest/v4 |
MCP_TRANSPORT_TYPE | Transport: stdio or http. | stdio |
MCP_HTTP_PORT | HTTP server port. | 3010 |
MCP_HTTP_ENDPOINT_PATH | HTTP endpoint path. | /mcp |
MCP_PUBLIC_URL | Public origin for TLS-terminating reverse-proxy deployments. | — |
MCP_AUTH_MODE | Auth mode: none, jwt, or oauth. | none |
MCP_LOG_LEVEL | Log level (debug, info, warning, error, etc.). | info |
MCP_GC_PRESSURE_INTERVAL_MS | Opt-in Bun-only forced-GC pressure loop (ms). Try 60000 if RSS grows under sustained HTTP load. | 0 |
LOGS_DIR | Directory for log files (Node.js only). | <project-root>/logs |
STORAGE_PROVIDER_TYPE | Storage backend: in-memory, filesystem, supabase, cloudflare-kv/r2/d1. | in-memory |
OTEL_ENABLED | Enable OpenTelemetry instrumentation. | false |
See .env.example for the full list of optional overrides.
Running the server
Local development
-
Build and run:
# One-time build bun run rebuild # Run the built server bun run start:stdio # or bun run start:http -
Run checks and tests:
bun run devcheck # Lint, format, typecheck, security bun run test # Vitest test suite bun run lint:mcp # Validate MCP definitions against spec
Docker
docker build -t courtlistener-mcp-server .
docker run --rm -e COURTLISTENER_API_TOKEN=your-token -p 3010:3010 courtlistener-mcp-server
The Dockerfile defaults to HTTP transport, stateless session mode, and logs to /var/log/courtlistener-mcp-server. OpenTelemetry peer dependencies are installed by default — build with --build-arg OTEL_ENABLED=false to omit them.
Project structure
| Directory | Purpose |
|---|---|
src/index.ts | createApp() entry point — registers tools and inits services. |
src/config | Server-specific environment variable parsing and validation with Zod. |
src/mcp-server/tools | Tool definitions (*.tool.ts). Ten tools across opinions, dockets, judges, courts, and oral arguments. |
src/services/courtlistener | CourtListener REST API client — auth, retry, rate-limit error classification. |
tests/ | Unit and integration tests mirroring src/. |
Development guide
See CLAUDE.md for development guidelines and architectural rules. The short version:
- Handlers throw, framework catches — no
try/catchin tool logic - Use
ctx.logfor request-scoped logging,ctx.statefor tenant-scoped storage - Register new tools via the barrels in
src/mcp-server/tools/definitions/index.ts - Wrap CourtListener API calls: validate raw → normalize to domain type → return output schema; never fabricate missing fields
Contributing
Issues and pull requests are welcome. Run checks and tests before submitting:
bun run devcheck
bun run test
License
Apache-2.0 — see LICENSE for details.