
CourtListener sits on 9 million US court opinions and this server exposes the full dataset through 12 tools. You can full-text search opinions with field-level filters, pull complete case text with all opinion variants (majority, concurrence, dissent), and traverse citation networks in both directions to map precedent chains. It also covers RECAP federal dockets with party and attorney search, judge biographical records including appointment history and financial disclosures, and oral argument audio with transcripts. The free tier gives you 125 requests per day, which is enough for targeted research but tight for deep citation traversal. Runs over stdio or as a hosted HTTP endpoint at courtlistener.caseyjhand.com/mcp.
claude mcp add --transport http courtlistener https://courtlistener.caseyjhand.com/mcpRun in your terminal. Add --scope user to make it available in every project.
Review the command, arguments, and environment values before installing — MCP servers run with your local permissions.
Verified live against the running server on Jun 10, 2026.
courtlistener_search_opinionsFull-text search across 9M+ written US court opinions with field-level filtering. Returns opinion cluster summaries with case metadata, citations, and matched text snippets. Supports CourtListener field syntax (caseName:"roe v wade", court_id:scotus, judge:"Alito") and boolean...8 paramsFull-text search across 9M+ written US court opinions with field-level filtering. Returns opinion cluster summaries with case metadata, citations, and matched text snippets. Supports CourtListener field syntax (caseName:"roe v wade", court_id:scotus, judge:"Alito") and boolean...
courtstringcursorstringfiled_afterstringfiled_beforestringorder_bystringscore desc · dateFiled desc · dateFiled asc · citeCount descdefault: score descpage_sizeintegerq*stringstatusstringPublished · Unpublished · Errata · Separate · In-chambers · Relating-tocourtlistener_get_opinionFetch the full text and metadata for a single opinion cluster by cluster ID. A cluster groups all opinions filed in a case — majority, concurrence, dissent, and per curiam. Returns all opinion variants with HTML and plain text. Obtain cluster IDs from courtlistener_search_opin...1 paramsFetch the full text and metadata for a single opinion cluster by cluster ID. A cluster groups all opinions filed in a case — majority, concurrence, dissent, and per curiam. Returns all opinion variants with HTML and plain text. Obtain cluster IDs from courtlistener_search_opin...
cluster_id*integercourtlistener_get_citationsRetrieve the citation network for an opinion cluster. Supports two directions: "cited_by" (opinions that cite this one — measures precedential influence) and "citing" (opinions this one cites — reveals the authority chain relied on). This is the primary tool for tracing legal...6 paramsRetrieve the citation network for an opinion cluster. Supports two directions: "cited_by" (opinions that cite this one — measures precedential influence) and "citing" (opinions this one cites — reveals the authority chain relied on). This is the primary tool for tracing legal...
cluster_id*integercourtstringcursorstringdirectionstringciting · cited_bydefault: cited_byfiled_afterstringpage_sizeintegercourtlistener_lookup_citationResolve a formatted legal citation string (e.g., "410 U.S. 113", "93 S. Ct. 705") to a cluster ID and case metadata. Enables workflows that start from a known citation rather than a search query. Supports standard US reporter formats. Requires authentication — uses the CourtLi...1 paramsResolve a formatted legal citation string (e.g., "410 U.S. 113", "93 S. Ct. 705") to a cluster ID and case metadata. Enables workflows that start from a known citation rather than a search query. Supports standard US reporter formats. Requires authentication — uses the CourtLi...
citation*stringcourtlistener_search_docketsSearch RECAP federal court dockets with party name, attorney, court, and date filters. RECAP is a crowd-sourced mirror of PACER (the federal court filing system) — coverage varies by court and date. Returns docket metadata with up to 3 sample document entries per docket. Use c...7 paramsSearch RECAP federal court dockets with party name, attorney, court, and date filters. RECAP is a crowd-sourced mirror of PACER (the federal court filing system) — coverage varies by court and date. Returns docket metadata with up to 3 sample document entries per docket. Use c...
courtstringcursorstringfiled_afterstringfiled_beforestringpage_sizeintegerparty_namestringq*stringcourtlistener_get_docketFetch full docket metadata and entry list for a single federal case by docket ID. Returns all available docket entries with document availability status. Documents with is_available=true have a RECAP-stored copy; others require a PACER account. Obtain docket IDs from courtlist...2 paramsFetch full docket metadata and entry list for a single federal case by docket ID. Returns all available docket entries with document availability status. Documents with is_available=true have a RECAP-stored copy; others require a PACER account. Obtain docket IDs from courtlist...
docket_id*integerentries_page_sizeintegercourtlistener_get_partiesFetch all parties and attorneys of record for a RECAP federal docket by docket ID. Returns each party's name, role (Plaintiff, Defendant, Petitioner, Respondent, etc.), and their attorneys with contact information. Requires two upstream calls per page (parties + attorney batch...3 paramsFetch all parties and attorneys of record for a RECAP federal docket by docket ID. Returns each party's name, role (Plaintiff, Defendant, Petitioner, Respondent, etc.), and their attorneys with contact information. Requires two upstream calls per page (parties + attorney batch...
docket_id*integerpageintegerpage_sizeintegercourtlistener_search_judgesSearch judge/person records by name, appointing president, court, political affiliation, or demographic. Returns biographical data, current position, and appointment summary. Use courtlistener_get_judge for full appointment history and education records.6 paramsSearch judge/person records by name, appointing president, court, political affiliation, or demographic. Returns biographical data, current position, and appointment summary. Use courtlistener_get_judge for full appointment history and education records.
appointerstringcourtstringcursorstringpage_sizeintegerpolitical_affiliationstringd · r · i · l · g · uq*stringcourtlistener_get_judgeFetch full biographical profile for a single judge: appointment history across all courts, education, political affiliations, and ABA ratings. Obtain person IDs from courtlistener_search_judges results.1 paramsFetch full biographical profile for a single judge: appointment history across all courts, education, political affiliations, and ABA ratings. Obtain person IDs from courtlistener_search_judges results.
person_id*integercourtlistener_lookup_courtsList courts with optional filtering by jurisdiction type and scraper status. Primarily used to discover court IDs for use in search and filter parameters across all other courtlistener tools. Returns court IDs, full names, citation strings, and scraper status.3 paramsList courts with optional filtering by jurisdiction type and scraper status. Primarily used to discover court IDs for use in search and filter parameters across all other courtlistener tools. Returns court IDs, full names, citation strings, and scraper status.
has_opinion_scraperbooleanin_usebooleanjurisdictionstringF · FD · FB · FBP · FS · Ccourtlistener_search_oral_argumentsSearch appellate oral argument audio recordings — the largest public collection of oral argument audio. Returns recording metadata with download URLs, panel judge IDs, and transcript snippets where available. Download URLs are direct MP3 links. Panel judge IDs can be passed to...6 paramsSearch appellate oral argument audio recordings — the largest public collection of oral argument audio. Returns recording metadata with download URLs, panel judge IDs, and transcript snippets where available. Download URLs are direct MP3 links. Panel judge IDs can be passed to...
argued_afterstringargued_beforestringcourtstringcursorstringpage_sizeintegerq*stringcourtlistener_get_oral_argumentFetch the full detail record for a single oral argument audio recording by its ID (the audio_id from courtlistener_search_oral_arguments). Returns the case name, panel judge IDs, duration, MP3 download URL, linked docket, and the speech-to-text transcript when transcription ha...1 paramsFetch the full detail record for a single oral argument audio recording by its ID (the audio_id from courtlistener_search_oral_arguments). Returns the case name, panel judge IDs, duration, MP3 download URL, linked docket, and the speech-to-text transcript when transcription ha...
id*integercourtlistener_search_financial_disclosuresSearch federal judicial financial disclosure filings — the annual reports judges file on investments, gifts, debts, outside positions, and income. Filter by judge (person ID from courtlistener_search_judges) and/or filing year. Returns per-filing metadata, category counts, ite...4 paramsSearch federal judicial financial disclosure filings — the annual reports judges file on investments, gifts, debts, outside positions, and income. Filter by judge (person ID from courtlistener_search_judges) and/or filing year. Returns per-filing metadata, category counts, ite...
cursorstringjudge_idintegerpage_sizeintegeryearintegerSearch 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
US case law and federal court records from CourtListener's 9M+ opinion corpus and its RECAP mirror of PACER. Search opinions and dockets, trace citation networks, resolve citations, look up judges and courts, and read oral argument transcripts and judicial financial disclosures. Runs as a stdio process, a local Streamable HTTP server, or the public hosted endpoint above.
| Tool | Description |
|---|---|
courtlistener_search_opinions | Full-text search across 9M+ court opinions with field syntax, court, date, and status filters |
courtlistener_get_opinion | Fetch an opinion cluster's metadata and the full text of every variant (majority, concurrence, dissent) |
courtlistener_get_citations | Trace a cluster's citation network: opinions it cites (citing) or that cite it (cited_by) |
courtlistener_lookup_citation | Resolve every citation in a text (e.g. "410 U.S. 113") to cluster IDs and case metadata |
courtlistener_search_dockets | Search RECAP federal dockets by case name, party, attorney, court, and filing date |
courtlistener_get_docket | Fetch a federal docket's metadata and paged entry list with document availability |
courtlistener_get_parties | Fetch the parties and attorneys of record on a RECAP docket |
courtlistener_search_judges | Search judges by name, appointing president, court, and political affiliation |
courtlistener_get_judge | Fetch a judge's positions, education, political affiliations, and ABA ratings |
courtlistener_lookup_courts | List courts by jurisdiction type, active/inactive status, and scraper coverage |
courtlistener_search_oral_arguments | Search appellate oral argument recordings by case name, transcript text, court, and argument date |
courtlistener_get_oral_argument | Fetch one oral argument's panel, duration, MP3 link, and speech-to-text transcript |
courtlistener_search_financial_disclosures | Search judicial financial disclosure filings by judge and year |
courtlistener_get_financial_disclosure | Fetch one disclosure's line items with coded values decoded to dollar ranges |
| Resource | Description |
|---|---|
courtlistener://reference/courts | Jurisdiction codes, common court IDs, search type codes, rate limits, and the maintenance window |
The jurisdiction codes are also listed on the jurisdiction parameter of courtlistener_lookup_courts, for clients that don't surface resources.
| Prompt | Description |
|---|---|
courtlistener_research_topic | Generate a structured legal research plan for a topic or question |
courtlistener_search_opinions toolq is required and takes CourtListener field syntax (caseName:"roe v wade", court_id:scotus, judge:"Alito") and AND / OR / NOT; filter by court, filed_after / filed_before, and status, and sort with order_by (score desc default, dateFiled desc, dateFiled asc, citeCount desc); 20 results per page, continued with next_cursorcluster_id (for courtlistener_get_opinion and courtlistener_get_citations), docket_id, cite_count, a snippet, and opinions[] variants with opinion ID, type, author_id, and local_path; totalCount and effectiveQuery report the matchcourtlistener_get_opinion toolcluster_id is required; after an outline response, sections: ["opinion_<id>", ...] pulls specific variants, and a name the cluster doesn't have fails as unknown_sectioncourt_id, citations, cite_count, precedential_status, syllabus, posture, docket_id) plus each variant's html_text, plain_text, cites[], download_url, and type_label, the same label courtlistener_search_opinions reportskind is full, or outline when the variants overflow the inline budget: the response then lists them as retrievable opinion_<id> sections and keeps the cluster metadatacourtlistener_get_citations toolcluster_id is required; direction is cited_by (default, opinions that cite this one) or citing (opinions it cites); optional court and filed_after filters; up to 20 results per page, continued with cursorcluster_id, court_id, date_filed, cite_count, and snippet; for citing, totalCount counts the unfiltered cited-opinion list, and a filtered page can come back empty while next_cursor is still setcourtlistener_lookup_citation toolcitation takes one citation or a passage of up to 64,000 characters, and every citation in it resolves; max_court_lookups (default 4, max 20, 0 to skip) caps the docket requests spent on court namesmatches[] entry per citation, with status (200 one case, 300 several candidates, 400 unrecognized reporter, 404 no match, 429 past the per-request citation cap) and status_label; each of its clusters[] carries cluster_id, docket_id, court_id, and court_resolution (resolved, no_docket, lookup_failed, over_budget, only the last worth retrying with a larger budget)not_found. CourtListener meters this endpoint per citation submitted, separately from the per-request limitscourtlistener_search_dockets toolq is required and matches case name, docket number, party, and attorney names; party_name (ANDed with q), court, and filed_after / filed_before narrow it; 20 results per page, continued with next_cursordocket_id, parties, attorneys, firms, suit_nature, assigned_to / referred_to, and up to 3 sample_documents with is_available and a RECAP filepath_local URL; every response adds a coverage_note on RECAP's partial PACER coveragecourtlistener_get_docket tooldocket_id is required; entries come 20 per page (CourtListener ignores entries_page_size), walked with entries_pagetotal_entries, next_cursor (the next entries_page number), and entries[] whose documents[] carry is_available, page_count, and filepath_local; documents with is_available: false need PACER, and fetching them is not exposedcourtlistener_get_parties tooldocket_id is required; cursor takes the opaque next_cursor token, not a page number; page_size (max 10) is a request CourtListener may not honorname, its docket-scoped role, and attorneys[] with name, contact_raw, role_code / role (codes 5–9 mean no longer of record), and date_action; total_parties uses the upstream total when supplied, otherwise counts a single-page result or stays nullcourtlistener_search_judges toolq is required; filter by appointer (the president's last name), court, and political_affiliation (d, r, i, l, g, u); 20 results per page, continued with next_cursorperson_id (for courtlistener_get_judge), political_affiliation and aba_rating as labels rather than codes, and a current_position: the one with no termination date, or the latest-starting when several or none qualifycourtlistener_get_judge toolperson_id is required; positions are fetched up to a page bound, and truncated with positionsShown reports when a long record ran past itjob_title and organization_name), education, political affiliations, ABA ratings, and fjc_id; coded fields keep the raw code beside a label (position_type_label, termination_reason_label, degree_label)dob_granularity, dod_granularity, date_start_granularity, and date_termination_granularity (year, month, or day)courtlistener_lookup_courts tooljurisdiction (one of 22 CourtListener codes such as F, FD, FB, S, SA; the full table is in courtlistener://reference/courts), status (active default, inactive, or any), and has_opinion_scraper; live records come 20 per page, walked with pagecourts[] carry id (the court ID every other tool filters on), citation_string, jurisdiction, and scraper flags; all_matching_court_ids lists every match from a bundled snapshot at no request cost, and is empty when more than 1,000 match, so check all_matching_court_ids_completecourtlistener_search_oral_arguments toolq is required and matches case name and transcript text; filter by court and argued_after / argued_before; 20 results per page, continued with next_cursoraudio_id (for courtlistener_get_oral_argument), panel_ids (for courtlistener_get_judge), duration_seconds, snippet, and two MP3 links: download_url at the originating court and local_path, CourtListener's durable copycourtlistener_get_oral_argument toolid (an audio_id from a search) is required; sections: ["transcript"] pulls a transcript that an outline response withheldpanel_ids, duration_seconds, download_url, docket_id, has_transcript, and transcript; kind is outline when the transcript overflows the inline budget. The argument date is not on this record; take it from the search result or the docketcourtlistener_search_financial_disclosures tooljudge_id (a person_id) and year; 20 filings per page, continued with cursor. year filters only the fetched page, because CourtListener has no server-side year filter, so keep paging when a page comes back empty with next_cursor setdisclosure_id, report_type, has_been_extracted, is_amended, pdf_url, per-category counts (investments, gifts, debts, positions, reimbursements, agreements, non-investment and spouse income), and itemized giftscourtlistener_get_financial_disclosure tooldisclosure_id is required; categories selects from investments, debts, positions, reimbursements, non_investment_incomes, spouse_incomes, agreements, and gifts (omit for all)counts, and the requested line-item rows, with coded value and income columns decoded to dollar ranges (N → $250,001 - $500,000); kind is outline when the full itemization overflows, listing categories by size for a categories re-callcourtlistener://reference/courts resourcecourtlistener_lookup_courts validates against), common court IDs, search type codes, the published free-tier rate limits, and the weekly maintenance window (Thursdays 21:00–23:59 PT)courtlistener_research_topic prompttopic required; jurisdiction optional (a court ID such as scotus or ca9); depth optional, overview (default, 3–5 key cases) or deep (adds citation-network traversal and judge lookup)Built on @cyanheads/mcp-ts-core: stdio and Streamable HTTP transports, pluggable auth (none / jwt / oauth), swappable storage (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), structured logging with optional OpenTelemetry tracing.
CourtListener-specific:
COURTLISTENER_RATE_LIMIT_PER_MINUTE and COURTLISTENER_RATE_LIMIT_PER_HOUR, so a burst waits instead of failing; a 429 pauses the queue for every waiting call, and a Retry-After is honored within a 45-second wait budgetcourtlistener_get_docket; two or more for courtlistener_get_parties and courtlistener_get_judge (one per extra page of a long roster or career); three for courtlistener_get_opinion and courtlistener_get_citations (plus one per extra page of opinion variants); and for courtlistener_lookup_citation, the lookup plus up to max_court_lookups docket requestsq, a malformed date, a citation over 64,000 characters, an unknown oral argument section) is rejected before a request is spentAgent-friendly output:
cluster_id, docket_id, person_id, audio_id, and disclosure_id appear wherever they feed a follow-up call, with field descriptions naming the tool that takes eachreason: "rate_limited" with retryAfter in seconds, whether CourtListener returned the 429 or the queue gave up before sending, in which case the message says no request was spentcourtlistener_get_opinion, courtlistener_get_oral_argument, and courtlistener_get_financial_disclosure return kind: "outline" with named sections to re-call forcoverage_note, court_resolution on citation lookups, truncated on position history, and notices when a page-local filter empties a page that has more pages behind itA 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"
}
}
}
Add the following to your MCP client configuration file. Generate a free API token in your CourtListener account settings.
{
"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
git clone https://github.com/cyanheads/courtlistener-mcp-server.git
cd courtlistener-mcp-server
bun install
cp .env.example .env
# edit .env and set COURTLISTENER_API_TOKEN
| Variable | Description | Default |
|---|---|---|
COURTLISTENER_API_TOKEN | Required. API token from your CourtListener account settings. | — |
COURTLISTENER_BASE_URL | API base URL override. | https://www.courtlistener.com/api/rest/v4 |
COURTLISTENER_RATE_LIMIT_PER_MINUTE | Requests the server starts per rolling minute. Past it, requests queue; one that can't start within about 45 s fails with the reset time. Raise it to match a higher token tier. | 5 |
COURTLISTENER_RATE_LIMIT_PER_HOUR | Requests the server starts per rolling hour. | 50 |
MCP_TRANSPORT_TYPE | Transport: stdio or http. | stdio |
MCP_HTTP_PORT | HTTP server port. | 3010 |
MCP_SESSION_MODE | HTTP session mode: stateless, stateful, or auto. The server declares stateless; setting this overrides it. | stateless |
MCP_AUTH_MODE | Authentication: none, jwt, or oauth. | none |
MCP_LOG_LEVEL | Log level (debug, info, warning, error, etc.). | info |
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. | false |
OTEL_EXPORTER_OTLP_ENDPOINT | Base endpoint for traces and metrics; appends /v1/traces and /v1/metrics. Without an endpoint, neither exports. | — |
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT | Explicit endpoint for redacted OTLP logs; the base endpoint does not enable log export. | — |
LOG_TOOL_FAILURE_PAYLOADS | Log failed-call inputs and results with key-based redaction. | false |
LOG_TOOL_FAILURE_PAYLOAD_MAX_BYTES | Size cap for each logged failed-call payload. | 16384 |
See .env.example for the full list of optional overrides.
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 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.
| Directory | Purpose |
|---|---|
src/index.ts | createApp() entry point: registers tools, resources, and prompts, and inits the service. |
src/config | Server-specific environment variable parsing and validation with Zod. |
src/mcp-server/tools | Tool definitions (*.tool.ts). 14 tools across opinions, citations, dockets, parties, judges, courts, oral arguments, and financial disclosures. |
src/mcp-server/resources | Resource definitions (*.resource.ts). Court reference resource. |
src/mcp-server/prompts | Prompt definitions (*.prompt.ts). Legal research prompt. |
src/services/courtlistener | CourtListener API client (request pacing, retry, error classification), code tables, and the bundled court snapshot (bun run courts:snapshot regenerates it). |
tests/ | Unit and integration tests mirroring src/. |
See CLAUDE.md for development guidelines and architectural rules. The short version:
try/catch in tool logicctx.log for request-scoped logging, ctx.state for tenant-scoped storagesrc/index.ts and adding them to the createApp({ tools, resources, prompts }) arraysIssues are welcome. Run checks and tests before submitting:
bun run devcheck
bun run test
Apache-2.0 — see LICENSE for details.
COURTLISTENER_API_TOKEN*CourtListener API token from your account settings. Required for API access.
MCP_LOG_LEVELdefault: infoSets the minimum log level for output (e.g., 'debug', 'info', 'warn').
MCP_HTTP_HOSTdefault: 127.0.0.1The hostname for the HTTP server.
MCP_HTTP_PORTdefault: 3010The port to run the HTTP server on.
MCP_HTTP_ENDPOINT_PATHdefault: /mcpThe endpoint path for the MCP server.
MCP_AUTH_MODEdefault: noneAuthentication mode to use: 'none', 'jwt', or 'oauth'.