Skip to main content
Glama

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

search_cases

Find cases by name, party, or docket number (e.g. "citizens united", "14-556"). Returns each case's Term + docket.

get_case

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.

list_term_cases

Every case from a given Term (e.g. "2014"), optionally with each case's stage, date, and one-line holding.

get_oral_argument

Oral-argument transcript, filterable by speaker, by text (find), and from a point in time (start). Handles multi-session arguments and timestamps.

get_opinion_announcement

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 above

  • speaker — 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 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. A truncated transcript ends by naming the exact part and start to continue from.

  • part — 1-based session index for arguments split across sessions (default: all)

  • include_timestamps — prefix each turn with H:MM:SS

  • max_chars — soft length cap, 1000–200000 (default 18000)

get_opinion_announcement

  • term, docket, speaker, find, start, part, include_timestamps, max_chars — as above. No speaker_type here; part is 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.txt

Optional, and worth it — confirm it works end to end before registering:

.\.venv\Scripts\python.exe selftest.py

macOS / 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.py

selftest.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-ui

From 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 the MCP_TIMEOUT environment variable, in milliseconds — MCP_TIMEOUT=60000 — before starting Claude Code.

  • uvx fails to fetch or build. It needs network access the first time, for the repo and the two dependencies. Run uvx --from git+https://github.com/attorneynate/oyez-mcp oyez-mcp in 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-run claude mcp add, or fix the config, pointing at the .venv interpreter.

  • "already exists at that scope" when re-adding. Remove the old entry first with claude mcp remove oyez --scope user, then add it again.

  • venv creation fails on Windows with a message about redirects or junctions. The folder is under AppData; 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 run search_cases and 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 tools
get_caseGet a Supreme Court caseA
Read-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.

ParametersJSON Schema
NameRequiredDescriptionDefault
termYes
docketYes

TDQS

A4.4/5.0
Behavior4/5

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.

Conciseness4/5

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.

Completeness4/5

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.

Parameters5/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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 transcriptA
Read-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).

ParametersJSON Schema
NameRequiredDescriptionDefault
findNo
partNo
termYes
startNo
docketYes
speakerNo
max_charsNo
include_timestampsNo

TDQS

A4.2/5.0
Behavior4/5

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.

Conciseness4/5

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.

Completeness4/5

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.

Parameters5/5

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.

Purpose5/5

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.

Usage Guidelines3/5

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 transcriptA
Read-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.

ParametersJSON Schema
NameRequiredDescriptionDefault
findNo
partNo
termYes
startNo
docketYes
speakerNo
max_charsNo
speaker_typeNo
include_timestampsNo

TDQS

A4.6/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters5/5

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.

Purpose5/5

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.

Usage Guidelines3/5

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 casesA
Read-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.

ParametersJSON Schema
NameRequiredDescriptionDefault
termYes
limitNo
include_summaryNo

TDQS

A4.5/5.0
Behavior5/5

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters5/5

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.

Purpose4/5

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.

Usage Guidelines4/5

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 casesA
Read-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).

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo
queryYes
include_peopleNo

TDQS

A4.7/5.0
Behavior4/5

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters5/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

  1. 5 tool updatesv0.1.0
    • First observedget_case
    • First observedget_opinion_announcement
    • First observedget_oral_argument
    • First observedlist_term_cases
    • First observedsearch_cases

TDQS

A4.4/5.0

Scored across 5 tools

Disambiguation4/5

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.

Naming Consistency5/5

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.

Tool Count5/5

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.

Completeness4/5

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

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers