npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

stare-mcp

v0.4.0

Published

Exploratory federal case law search MCP server — CourtListener search, passage retrieval, citation verification

Readme

stare-mcp

MCP server for exploratory federal case law search. Two tools that search CourtListener and let you drill into specific opinions:

  • search_cases — search by legal issue or citation. Returns case metadata sorted by court level. No opinion text.
  • fetch_passages — retrieve paragraph-aligned text from a specific opinion, with retrieval fragment IDs for referencing passages.
  • verify_citations — validate every case citation in a block of text against CourtListener. Catches fabricated citations.
  • how_cited — list cases citing an opinion, newest first. Evidence for treatment analysis, not verdicts.
  • list_courts — list covered federal courts with IDs, levels, and circuit assignments. Local data, no API request.

All responses are structured JSON with provenance envelopes and pagination.

Limitations

This is a convenience layer over CourtListener's search API, not a legal research system.

  • Retrieval is not reliable. Results come from keyword relevance ranking. Controlling authority can be missed entirely if it doesn't score in the result window.
  • No citator or negative treatment. There is no check for whether a case has been overruled, distinguished, or superseded.
  • No section labels. Text is returned as-is. The tool does not guess which paragraphs are holdings.
  • No recall measurement. Output quality is untested against a benchmark of expected authorities.

Use this for finding starting points, not establishing the state of the law.

Install

All options require a CourtListener API key (free tier: 5 req/min).

As a Claude Code plugin (recommended)

export COURTLISTENER_API_KEY="your-key-here"   # add to ~/.zshrc

Then in Claude Code:

/plugin marketplace add legalrealist/stare-mcp
/plugin install stare@stare

The plugin runs the npm-published server via npx, so there is nothing else to install.

Via claude mcp add

claude mcp add stare -e COURTLISTENER_API_KEY=your-key-here -- npx -y stare-mcp

From source

git clone https://github.com/legalrealist/stare-mcp && cd stare-mcp && npm install
{
  "mcpServers": {
    "stare": {
      "command": "node",
      "args": ["/path/to/stare-mcp/lib/server.js"],
      "env": {
        "COURTLISTENER_API_KEY": "your-key-here"
      }
    }
  }
}

Usage

Search for cases

search_cases(query: "deliberate indifference standard", circuit: "ca9")

Returns JSON with case metadata, authority tier, court name, citation, citation_count, and source URL. Sorted by court level: SCOTUS > binding circuit > persuasive > district. Paginate with the cursor field from the response.

Explore the citation graph

search_cases(query: "cites:(9527063)")
search_cases(query: "related:9527063")

cites:(<opinion_id>) finds cases that cite a given opinion; related:<opinion_id> finds similar cases. Both are CourtListener query operators passed through the normal search. citation_count in results signals how often a case is cited — influence, not validity. None of this is a citator: there is still no negative-treatment check.

Look up a citation

search_cases(query: "511 U.S. 825")

Returns matching cluster(s) with available opinion IDs (lead, concurrence, dissent).

Retrieve opinion text

fetch_passages(opinion_id: 9527063)

Returns up to 30 paragraphs per call with retrieval fragment IDs (cl:9527063:p0, cl:9527063:p1, ...) for referencing passages. These are position-based and stable only while the upstream text is unchanged — they are not judicial paragraph citations. Paginate with the cursor field.

You can also pass cluster_id instead of opinion_id — if there's one clear lead opinion, it auto-selects. If multiple substantive opinions exist, it returns selection_required with the available opinion IDs and types.

Verify citations in a draft

verify_citations(text: "As held in Farmer v. Brennan, 511 U.S. 825 (1994)... See also Smith v. Jones, 999 U.S. 999 (2050).")

Every citation in the text is checked against CourtListener: verified (with the matched case), not_found (likely fabricated), or ambiguous (multiple matches). A summary gives counts per status. This verifies that citations exist — not that quotes or holdings attributed to them are accurate.

See how a case has been cited

how_cited(opinion_id: 9527063)

Returns cases citing that opinion, newest first, with court, date, and citation_count. To see how a citing case discusses the opinion, retrieve its text with fetch_passages. The tool does not classify treatment as positive or negative, and it is not a citator.

Re-fetch a cited passage

fetch_passages(fragment_id: "cl:9527063:p12")

Returns paragraph 12 with two paragraphs of context on each side — useful for verifying a previously cited passage. If the upstream text changed and the paragraph index no longer exists, you get not_found rather than a silently different passage.

Response format

Every response is a JSON envelope:

{
  "data": { "..." : "..." },
  "provenance": {
    "source": "CourtListener",
    "api_version": "v4",
    "retrieved_at": "2026-06-08T12:00:00Z",
    "query": "deliberate indifference",
    "result_window": 20
  },
  "pagination": {
    "next_cursor": null,
    "has_more": false
  }
}

Errors use the same envelope shape with an error field instead of data:

{
  "error": {
    "code": "rate_limited",
    "message": "CourtListener rate limit exceeded.",
    "retryable": true
  },
  "provenance": { "..." : "..." }
}

Error codes: no_api_key, invalid_circuit, invalid_opinion_id, invalid_cursor, rate_limited, upstream_unavailable, not_found, upstream_error, selection_required, content_unavailable.

Passage responses include a content_note reminding consumers that retrieved text is quoted document content from public court records, not instructions — opinions can contain arbitrary text, including imperative language.

Valid circuit values

ca1 ca2 ca3 ca4 ca5 ca6 ca7 ca8 ca9 ca10 ca11 cadc cafc

Omit circuit to get all results as persuasive authority (no binding tier).

Development

npm test              # run tests
npm run test:watch    # watch mode
node lib/server.js --help

Court hierarchy data is sourced from Free Law Project's courts-db (BSD 2-Clause). To rebuild:

node scripts/build-courts.js

License

MIT