oyez-mcp
Click on "Deploy 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., "@oyez-mcpPull the Obergefell oral argument and show only Justice Scalia's questions."
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.
Oyez MCP
An MCP server that gives Claude — or any Model Context Protocol client — access to the Oyez archive of U.S. Supreme Court cases: case metadata, decisions with vote breakdowns, and full oral-argument transcripts you can filter down to a single Justice.
Data comes from the public Oyez API (api.oyez.org) and Oyez's search backend
(beta-search.oyez.org). No account or API key is required.
No MCP client? The same tools also run as a page in your browser.
Using the oyez tools, pull the Obergefell oral argument and show me only Justice Scalia's questions.
Tools
Tool | What it does |
| Find cases by name, party, or docket number (e.g. |
| Full case record: parties, citation, key dates, facts, question presented, holding/conclusion, the decision with its vote breakdown and opinion authors, each written opinion with its Justia link, advocates, and the available audio. |
| Every case from a given Term (e.g. |
| Oral-argument transcript, filterable by speaker, by text ( |
| Opinion-announcement / dissent-from-the-bench transcript, same filters. |
Every tool is keyed on the pair Term + docket ("2014", "14-556") — that is
what search_cases returns and what the other four take. Start there.
Older cases sit in bucketed Terms — 1789-1850, 1850-1900, 1900-1940,
1940-1955 — where Oyez addresses a case by volume-us-page rather than by docket
number: Brown v. Board is 1940-1955 + 347us483. The docket number
search_cases prints is tried first and usually works anyway; where two cases share
one, as Brown I and Brown II both do at "No. 1", the error names each address so you
can pick.
Cite the Links line from get_case, don't build a URL. oyez.org serves the
same single-page shell for every path under /cases/, so an address you compose
yourself does not 404 when it is wrong — it answers 200 and renders an empty page.
Parameters
search_cases
query— case name, party name, or docket number. An original-jurisdiction docket can be written"156-orig","No. 156, Orig.", or"22O156".limit— 1–50 (default 10)include_people— also return matching Justices and advocates (default false)
get_case
term— the Term year, e.g."2014"docket— e.g."14-556"
list_term_cases
term— the year the Term began, so"2014"means OT2014 (October 2014 through June/July 2015)limit— 1–400 (default 60)include_summary— also give each case's stage and date (decided, argued, or granted) and its one-line holding, or its question presented before a decision (default false). About 300 characters more per case, and the way to browse a Term by topic.
Where cases in a Term share a docket number, as Brown I and Brown II share "No. 1",
the line for each one gives the get_case call that reaches it.
get_oral_argument
term,docket— as abovespeaker— case-insensitive substring of a name; returns only that person's turns ("Scalia","Verrilli")speaker_type—"justice"or"advocate". Someone who argued the case before joining the Court — Kagan as Solicitor General in Citizens United — counts as an advocate there.find— case-insensitive text; returns only the turns that contain it ("personhood"). Combine withspeaker_typefor what one side said about it.start— resume at this point in the recording, as"H:MM:SS","M:SS", or seconds. Turns before it in the first selected session are skipped. A truncated transcript ends by naming the exactpartandstartto continue from.part— 1-based session index for arguments split across sessions (default: all)include_timestamps— prefix each turn withH:MM:SSmax_chars— soft length cap, 1000–200000 (default 18000)
get_opinion_announcement
term,docket,speaker,find,start,part,include_timestamps,max_chars— as above. Nospeaker_typehere;partis how you pick between, say, the majority announcement and a dissent read from the bench.
A note on search: Oyez's index matches case names, parties, and docket numbers. It is
not a free-text topical search. "brown v board of education" and "14-556" work
well; a bare topic like "abortion" only finds cases with that word in the title. The
exception is the four recent Terms the server scans itself, where a query is also
matched against each case's one-line holding and question presented, so
"universal injunction" does find Trump v. CASA; such a result shows the line it
matched. For a topic in an older Term, list the Term with include_summary.
Oyez's own search index runs well behind its case data (in September 2026 it had no
2025 Term case at all, and only 21 of the 62 in the 2024 Term), so search_cases also
scans the four most recent Terms' case lists — the coming Term, the current one, and
the two before it — by name, docket number, holding, and question presented, and lists
those matches first. A case
decided this Term is found by its name or its docket number like any other.
Transcripts are long. A full argument can run a hundred thousand characters, so
filter with speaker, speaker_type, or find when you only need part of it. To
read one straight through, keep the default cap and follow the note at the end of a
truncated transcript: it names the part and start to continue from. Raise
max_chars deliberately rather than by habit.
Related MCP server: @cyanheads/courtlistener-mcp-server
Requirements
An MCP client — Claude Code (CLI or the desktop app's Code tab), Claude Desktop, or anything else that speaks MCP over stdio. Or none at all: the browser page needs only a web browser.
uv, or Python 3.10 or newer
Install
With uv
Nothing to clone and no paths to spell out. uvx fetches the repo, builds it,
installs its two dependencies into an isolated environment of its own, and runs the
server's oyez-mcp command. The first start does that work and can take a minute;
later starts come from the cache.
For Claude Code, run this in a normal terminal, not inside a Claude Code session:
claude mcp add --scope user oyez -- uvx --from git+https://github.com/attorneynate/oyez-mcp oyez-mcp--scope user registers the server once for all your projects; drop the flag to
register it in the current project only. Oyez's data spans everything, so user scope
usually makes sense. Check it with claude mcp list, then start Claude Code and run
/mcp in the session — oyez should show as connected with its five tools.
For Claude Desktop and other MCP clients, add a stdio server to the client's config.
For Claude Desktop that is claude_desktop_config.json, reachable from
Settings → Developer → Edit Config:
{
"mcpServers": {
"oyez": {
"command": "uvx",
"args": ["--from", "git+https://github.com/attorneynate/oyez-mcp", "oyez-mcp"]
}
}
}Restart the client afterward. If it reports that it cannot find uvx, give the
command's full path instead; where uvx on Windows or which uvx elsewhere prints
it.
From source
This is the path for working on the server, or for a machine without uv. Clone it, make a virtual environment, install two dependencies.
Windows (PowerShell)
Keep the folder somewhere outside AppData. The Windows Store build of Python
redirects AppData paths, which breaks venv creation there; your home directory is
fine.
git clone https://github.com/attorneynate/oyez-mcp.git
cd oyez-mcp
python -m venv .venv
.\.venv\Scripts\python.exe -m pip install -r requirements.txtOptional, and worth it — confirm it works end to end before registering:
.\.venv\Scripts\python.exe selftest.pymacOS / Linux
git clone https://github.com/attorneynate/oyez-mcp.git
cd oyez-mcp
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt.venv/bin/python selftest.pyselftest.py starts the server over stdio, lists its tools, and pulls a real
transcript. If it prints All good, the server itself works and anything that goes
wrong next is configuration.
Then point the client at the venv's Python and the absolute path to server.py.
There is no activation step — the interpreter path is the activation.
For Claude Code, from the repo folder in a normal terminal:
claude mcp add --scope user oyez -- "$(pwd)/.venv/bin/python" "$(pwd)/server.py"On Windows, spell out the absolute paths:
claude mcp add --scope user oyez -- "C:\path\to\oyez-mcp\.venv\Scripts\python.exe" "C:\path\to\oyez-mcp\server.py"For Claude Desktop and other clients:
{
"mcpServers": {
"oyez": {
"command": "/absolute/path/to/oyez-mcp/.venv/bin/python",
"args": ["/absolute/path/to/oyez-mcp/server.py"]
}
}
}On Windows, JSON needs the backslashes doubled:
{
"mcpServers": {
"oyez": {
"command": "C:\\path\\to\\oyez-mcp\\.venv\\Scripts\\python.exe",
"args": ["C:\\path\\to\\oyez-mcp\\server.py"]
}
}
}Restart the client afterward. Neither config expands ~ or other shell shortcuts —
use full absolute paths.
Use it in a browser
The same five tools also come as a web page that runs on your own computer, for reading cases and transcripts without an MCP client:
uvx --from git+https://github.com/attorneynate/oyez-mcp oyez-uiFrom a clone, run python ui.py with the venv's Python instead. Your browser opens
on the page, and Ctrl+C in the terminal stops it.
Search by case name, party, or docket number, or browse a Term with each case's one-line holding. Click a case to open it.
Case shows the facts, question, conclusion, each Justice's vote, the written opinions, and links to Oyez and Justia.
Oral argument and Announcement show the transcripts. Filter by speaker (or click a name in the roster), by Justices or advocates, or by a word, and show timestamps if you want them. A long transcript loads a chunk at a time; Load more picks up where it stopped.
The address bar holds the case and tab, so a view can be bookmarked.
The page listens on 127.0.0.1 only, port 8765 or a free one if that is taken
(--port picks another, --no-browser skips opening a tab). Nothing else on your
network can reach it, and it has no login because it needs none.
Example prompts
"Using the oyez tools, pull the Obergefell oral argument and show me only Justice Scalia's questions."
"Search Oyez for New York Times v. Sullivan and summarize the holding and the vote."
"Get the opinion announcement for Obergefell and quote the part Roberts read from the bench."
"List the 2014 Term cases, then give me the vote breakdown for Glossip v. Gross."
"In the Citizens United argument, what did the advocates say about corporate personhood? Advocates only."
"List the 2024 Term with summaries and pick out the First Amendment cases."
"Read me the Obergefell argument from the start, a chunk at a time."
Troubleshooting
Slow first start. A stdio server's first launch can exceed Claude Code's 30-second startup timeout, and
uvx's first start also fetches and builds the package. Raise it with theMCP_TIMEOUTenvironment variable, in milliseconds —MCP_TIMEOUT=60000— before starting Claude Code.uvxfails to fetch or build. It needs network access the first time, for the repo and the two dependencies. Runuvx --from git+https://github.com/attorneynate/oyez-mcp oyez-mcpin a terminal to see the error; a server that starts then sits waiting for input, so end it with Ctrl+C.No module named 'mcp'. You registered a system Python instead of the venv's. Re-runclaude mcp add, or fix the config, pointing at the.venvinterpreter."already exists at that scope" when re-adding. Remove the old entry first with
claude mcp remove oyez --scope user, then add it again.venvcreation fails on Windows with a message about redirects or junctions. The folder is underAppData; move it to a normal path and recreate the venv.A tool answers
Not found on Oyez. The Term/docket pair is wrong. Tools return errors as readable text instead of crashing, so runsearch_casesand use the Term and docket it hands back.Missing or garbled speaker names in a transcript. Oyez's speaker attribution is thinner for older cases. That is the source data, not the server.
How it works
server.py is a single-file stdio MCP server; pyproject.toml packages it so
uvx can fetch and run it, and the oyez-mcp command it defines is server.main(),
the same thing python server.py runs. It queries the same endpoints
oyez.org's own front end uses, then formats the JSON into Markdown for the model
rather than passing raw API responses through: a decision becomes a list of each
Justice's vote, what they wrote, and whose opinions they joined, and transcripts
become Speaker: text turns.
ui.py is the browser page: a small Starlette app, served by uvicorn (both come
with mcp), that calls those same tool functions and renders their Markdown. It
adds no Oyez logic of its own.
There is no database, and every tool call is a live HTTP request, with one exception:
a Term's case list, once fetched, is kept in memory for ten minutes. That covers the
four recent Terms search_cases scans, any Term list_term_cases lists, and the Term
get_case searches when a docket number is not the case's address. max_chars
exists because full transcripts are big enough to matter to a context window.
Contributing
Bug reports and pull requests are welcome — see CONTRIBUTING.md.
License
MIT. See LICENSE.
Disclaimer
This uses unofficial Oyez endpoints. Oyez is a free law project, a collaboration of Cornell's Legal Information Institute, Justia, and Chicago-Kent College of Law; this server is not affiliated with it, endorsed by it, or supported by it. The endpoints can change or rate-limit without notice, so use this for research and education and be a considerate client.
Nothing here is legal advice. Oyez's case summaries are secondary sources written for a general audience — if you are citing the Court, go to the opinion.
Available Tools
5 toolsget_caseGet a Supreme Court caseARead-onlyIdempotent
Full details for one Supreme Court case: parties, citation, key dates, facts, the question presented, the holding/conclusion, the decision with its vote breakdown and opinion authors, the advocates, and a list of the available oral-argument and opinion-announcement audio.
"Written opinions" links each opinion's text on Justia: the syllabus, the opinion of the Court, and every concurrence and dissent, by author.
The "Links" line is the address to cite or open. Take it as printed rather than building one from the Term and docket: oyez.org serves the same single-page shell for every path under /cases/, so a composed address that is wrong still answers 200 and renders an empty page.
Args: term: The Term year, e.g. "2014" (from search_cases). docket: The docket number, e.g. "14-556" (from search_cases). Older cases sit in bucketed Terms (1789-1850, 1850-1900, 1900-1940, 1940-1955) where Oyez addresses a case by volume-us-page instead, e.g. term "1940-1955", docket "347us483" for Brown. The docket number search_cases printed is tried first and usually works; when two cases share one, the error names both addresses.
| Name | Required | Description | Default |
|---|---|---|---|
| term | Yes | ||
| docket | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (readOnlyHint, idempotentHint, openWorldHint), yet the description adds real operational context: a warning that composing an oyez.org address can silently render an empty 200 page, and the disclosure that duplicate docket numbers produce an error naming both candidate addresses. It stops short of return-shape or pagination detail, but the failure-mode disclosure is unusually valuable.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the return contents, then the citation caveat, then the args, which is a sensible order for an agent. The prose is information-dense but the paragraph on the 'Links' line and the bucketed-Terms passage are both slightly long for the payload they carry.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description carries the return-value burden and does so by enumerating the case fields and the linked opinion texts. Arguments are fully specified for a 0%-coverage schema. Minor gaps remain around not-found handling and audio payload specifics, but nothing blocks a correct call.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0% and the two properties carry no descriptions, so the description must compensate and does: it gives the term format ('2014'), the docket format ('14-556'), explains the bucketed Terms (1789-1850 through 1940-1955) where volume-us-page addressing is used, and describes retry/fallback behavior on duplicate dockets.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Full details for one Supreme Court case') and then enumerates exactly what is returned: parties, citation, dates, facts, question presented, holding, vote breakdown, opinion authors, advocates, and audio. An agent can tell this apart from search_cases or get_oral_argument without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Clearly indicates the inputs come from search_cases and gives an explicit fallback path for older bucketed Terms, including the Brown / '347us483' example. It does not, however, state explicit when-not-to-use conditions or contrast itself against get_opinion_announcement or get_oral_argument, which also surface audio.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_opinion_announcementGet an opinion-announcement transcriptARead-onlyIdempotent
Fetch the opinion-announcement transcript(s) for a case - the Justices announcing the decision (and any dissents read from the bench).
Args: term: The Term year, e.g. "2014". docket: The docket number, e.g. "14-556". speaker: Case-insensitive substring of a speaker's name to include only their turns. find: Case-insensitive text; include only the turns that contain it. start: Resume at this point, as "H:MM:SS", "M:SS", or seconds; turns before it in the first selected announcement are skipped. A truncated transcript ends by naming the part and start to continue from. part: 1-based index when several announcements exist (e.g. the majority announcement and a separate dissent); default is all. include_timestamps: Prefix each turn with its start time. max_chars: Soft cap on length, 1000-200000 (default 18000).
| Name | Required | Description | Default |
|---|---|---|---|
| find | No | ||
| part | No | ||
| term | Yes | ||
| start | No | ||
| docket | Yes | ||
| speaker | No | ||
| max_chars | No | ||
| include_timestamps | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover safety (readOnly, idempotent, openWorld), but the description adds real operational context: max_chars is a 'soft cap', a truncated transcript 'ends by naming the part and start to continue from', and 'start' skips turns before it in the first selected announcement. This continuation protocol is exactly the kind of behavior structured fields can't convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the purpose sentence followed by a compact Args list; each entry is a single useful line. Slight redundancy in restating parameter names, but nothing is wasted.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For an 8-parameter, no-output-schema tool, the description covers purpose, all parameters, and the truncation/continuation flow. It could say a bit more about the shape of returned 'turns', but the essentials an agent needs to call it correctly are present.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With 0% schema description coverage the description fully compensates, documenting all 8 parameters with format examples ('2014', '14-556'), semantics for start (H:MM:SS, M:SS, or seconds), part as a 1-based index, defaults for part/include_timestamps/max_chars, and the substring/text matching behavior of speaker and find.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource (fetch the opinion-announcement transcript for a case) and elaborates with 'Justices announcing the decision (and any dissents read from the bench).' This clearly distinguishes it from the sibling get_oral_argument, which covers a different transcript type.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied by the resource name and by the 'part' explanation covering multiple announcements, but the description never says when to choose this over get_oral_argument, get_case, or search_cases, and gives no prerequisites or exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_oral_argumentGet an oral-argument transcriptARead-onlyIdempotent
Fetch the oral-argument transcript for a case, optionally filtered by speaker, by text, or from a point in time.
The transcript is returned as "Speaker: text" turns. Use the filters to zero in on, for example, one Justice's questions ("only Justice Scalia's questions" -> speaker="Scalia") or every turn that mentions a word (find="personhood"). A full argument is far longer than the default cap; to read one straight through, follow the note at the end of a truncated transcript, which names the part and start to continue from.
Args: term: The Term year, e.g. "2014". docket: The docket number, e.g. "14-556". speaker: Case-insensitive substring of a speaker's name; include only their turns (e.g. "Scalia", "Verrilli", "Roberts"). speaker_type: "justice" or "advocate" to include only that group. Someone who argued the case before joining the Court (Kagan as Solicitor General, say) counts as an advocate. find: Case-insensitive text; include only the turns that contain it (e.g. "corporate", "personhood"). Combine with speaker_type for what one side said about it. start: Resume at this point in the recording, as "H:MM:SS", "M:SS", or seconds. Turns before it in the first selected session are skipped; give part as well when there are several sessions. part: For arguments split into sessions, the 1-based session index; default is all sessions. include_timestamps: Prefix each turn with its start time (H:MM:SS). max_chars: Soft cap on transcript length, 1000-200000 (default 18000). When hit, output is truncated with a note that says where to continue from.
| Name | Required | Description | Default |
|---|---|---|---|
| find | No | ||
| part | No | ||
| term | Yes | ||
| start | No | ||
| docket | Yes | ||
| speaker | No | ||
| max_chars | No | ||
| speaker_type | No | ||
| include_timestamps | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover read-only, open-world, and idempotent behavior. The description adds substantial context beyond that: the transcript is returned as 'Speaker: text' turns, output is truncated at max_chars with a note explaining how to continue, speaker_type has a nuanced definition, and session/part semantics are described.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the core action and return format, then uses a well-organized Args section. Despite its length, every sentence provides necessary operational detail for a nine-parameter tool with no schema descriptions.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity, nine parameters, no output schema, and zero schema-description coverage, the description is complete enough for correct invocation. It covers return format, filtering, truncation, continuation, session handling, and timestamp behavior.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must carry the full burden. It does this thoroughly: every one of the nine parameters is explained with examples, formats, defaults, and edge cases (e.g., speaker_type counts Kagan as an advocate, start accepts H:MM:SS/M:SS/seconds).
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: 'Fetch the oral-argument transcript for a case.' It also identifies the optional filtering dimensions, making it easy to distinguish from sibling tools like search_cases or get_case.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives concrete examples of how to use filters (e.g., speaker="Scalia", find="personhood") and explains the truncation-continuation workflow, but it never explicitly says when to choose this tool over siblings such as get_case or search_cases. Usage is implied rather than contrasted with alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_term_casesList a Term's casesARead-onlyIdempotent
List the Supreme Court cases from a given Term, in order of argument.
Each line gives the docket number to pass to get_case. Where cases in the Term share one (Brown I and Brown II are both "No. 1"), the line gives the get_case call that reaches that case instead.
Args: term: The Term year - the year the Term began, e.g. "2014" for OT2014 (October 2014 through June/July 2015). limit: Maximum cases to list, 1-400 (default 60). include_summary: Also give each case's stage and date (decided, argued, or granted) and its one-line holding, or its question presented before a decision. About 300 characters more per case; the way to browse a Term by topic.
| Name | Required | Description | Default |
|---|---|---|---|
| term | Yes | ||
| limit | No | ||
| include_summary | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the readOnly/idempotent/openWorld annotations by disclosing output shape (one line per case, docket number for get_case), ordering by argument, the duplicate-docket edge case (Brown I/II) and how lines route around it, and the roughly 300-character cost per case of include_summary. That is real operational context an agent cannot get from the structured fields.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the core purpose, then uses an Args block for the parameters; the prose is tight and each sentence adds information. The only minor waste is restating the limit default, which is already present in the schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description still explains the return format, ordering, and the gotcha around shared docket numbers, and it defines every parameter. An agent has everything needed to call this correctly and interpret the result.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description carries the full burden and does: 'term' is defined as the year the Term began with a concrete example ("2014" = OT2014), and 'limit' is bounded at 1-400 with a stated default. include_summary's effect and cost are explained rather than just named.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource (list Supreme Court cases) with an explicit scope and ordering ('from a given Term, in order of argument'), which makes it distinguishable from get_case in practice. It does not name the closest sibling, search_cases, so the differentiation is implied rather than stated.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Clear context: this is the Term-browsing tool, and the description explains that include_summary is 'the way to browse a Term by topic'. It also tells the agent where the output goes next (docket numbers feed get_case), but it never states when to prefer search_cases instead, so there are no explicit exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_casesSearch Supreme Court casesARead-onlyIdempotent
Search Oyez for U.S. Supreme Court cases by case name, party, or docket number.
This mirrors the search box on oyez.org: it matches case names, party names, and docket numbers - not free-form topics. "citizens united", "brown v board of education", and "14-556" all work well; a bare topic like "abortion" only finds cases with that word in the title. Every result lists the Term and docket number you pass to get_case, get_oral_argument, or list_term_cases.
Oyez's search index runs well behind its case data, so this tool also scans the four most recent Terms' case lists and lists those matches first. There it matches the one-line holding and the question presented as well as the name and docket, so a topic ("birthright citizenship", "universal injunction") does find a recent case; such a row shows the line it matched. Older cases still need a name, party, or docket number.
Args: query: Case name, party name, or docket number (e.g. "Obergefell", "new york times v sullivan", "14-556"). An original-jurisdiction docket can be written "156-orig", "No. 156, Orig.", or "22O156". limit: Maximum number of cases to return, 1-50 (default 10). include_people: Also include matching Justices/advocates (default False).
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| query | Yes | ||
| include_people | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the readOnly/openWorld/idempotent annotations by disclosing the search-index lag, the fallback scan of the four most recent Terms, and the extra fields matched there (holding, question presented). It also explains why a matched row shows the matching line — a genuinely non-obvious behavior. Slightly short of 5 only because it does not describe result ordering beyond 'recent matches first' or any result-count behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the core purpose, then search semantics, then the index-lag caveat, then Args — a clean progression. The middle paragraph is dense but every sentence carries behavioral information; the only cost is overall length for a three-parameter tool.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description still tells the agent what a result row contains (Term, docket number, matched line) and how to use those values downstream. Combined with the query semantics and the recency caveat, an agent can call this tool correctly without further context.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0%, so the description carries the full burden and does: it gives the semantic scope of 'query' with examples and alternate docket syntax ('156-orig', 'No. 156, Orig.', '22O156'), the valid range and default for 'limit' (1-50, default 10), and the meaning/default of 'include_people'. Nothing an agent needs to fill the params is missing.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Search Oyez for U.S. Supreme Court cases') and immediately scopes what is matched (case name, party, docket). It also names the downstream siblings (get_case, get_oral_argument, list_term_cases), so an agent can place it in the workflow without opening other schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly defines when the tool works and when it does not: name/party/docket match well, bare topics only work against titles for the four most recent Terms, and older cases require a name, party or docket. Concrete worked examples ('citizens united', '14-556') remove ambiguity about query form.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
5 tool updates
v0.1.0- First observed
get_case - First observed
get_opinion_announcement - First observed
get_oral_argument - First observed
list_term_cases - First observed
search_cases
TDQS
Scored across 5 tools
Each tool targets a distinct resource or action: search by query, list by Term, fetch case details, fetch oral argument transcript, and fetch opinion announcement transcript. The only mild overlap is between search_cases and list_term_cases (both return case lists), but their descriptions clearly differentiate query-based search from Term browsing.
All tool names use consistent snake_case with a clear verb_noun pattern: get_oral_argument, search_cases, get_case, list_term_cases, get_opinion_announcement. The verbs (get, search, list) are used appropriately and unambiguously.
Five tools is well-scoped for a read-only legal data server, covering search, browse, case details, and two transcript types. Each tool has a clear, non-redundant purpose, and the count is comfortably within the ideal range.
The surface covers the core workflows: finding cases, listing Terms, retrieving case details, and fetching both oral argument and opinion announcement transcripts. Minor gaps exist—no direct retrieval of opinion text (only links) or audio recordings—but these are workable through the provided details.
Maintenance
Related MCP Connectors
Search U.S. case law, fetch opinions, and ask matter-aware legal questions over your documents.
Search public U.S. federal litigation: companies, cases, dockets and document metadata.
Search US court opinions, federal dockets, judges, citations, and oral arguments via CourtListener.
Search US federal and state court records through CourtListener and the RECAP archive
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables legal research across 3,352 U.S. courts using the CourtListener API, providing access to case search, precedent analysis, judge patterns, citation validation, and federal PACER dockets through natural language queries.6 npm3MIT
- AlicenseNot gradedqualityAmaintenanceSearch and retrieve US court opinions, federal dockets, judge records, citation networks, and oral arguments from CourtListener's 9M+ opinion corpus via MCP.140 npm3Apache 2.0
- AlicenseNot gradedqualityBmaintenanceEnables users to search and retrieve court docket records across US state, county, and federal courts, including PACER party searches, and to get full case details.8 npmMIT
- AlicenseNot gradedqualityBmaintenanceEnables searching and retrieving metadata from U.S. federal appellate court dockets, including party name search and recent dockets.202 npmMIT