To compose legal MCPs, add each server to one client's mcpServers config (CourtListener for US case law, CanLII for Canadian decisions, a statutes server for US primary law), then add one short router prompt that tells the model which server owns which jurisdiction. The client merges all three tool catalogs into a single toolbox, and a cross-jurisdiction legal research workflow runs in one chat thread. The full config and routing prompt are below.
Reviewed and corrected on 12 October 2026. This is general information, not legal advice; check the cited statute or opinion before relying on it.
TL;DR
- A legal MCP workflow built on three servers covers three data lanes: US case law and dockets via CourtListener, Canadian decisions and legislation metadata via CanLII, and US primary law (US Code, CFR, federal and state court rules, state statutes and regulations, Executive Orders) via a statutes server.
- Running multiple MCP servers for legal research is a configuration problem plus one short system prompt that tells the model which server to open first.
- That router prompt does the work many teams try to solve with custom agents or state machines.
- The hard parts are rate-limit budgeting, key management, and verification of whatever the model returns.
A colleague wants a hosted legal MCP server, which is just a URL, available inside Claude Desktop. Where does Anthropic's documentation say to add it?
Part of our MCP and developer guide series.
New to single-server setup first? Start with the CourtListener MCP and the CanLII MCP, then come back here to compose them. If you have never connected an MCP server to Claude, use Claude for US legal research over MCP walks the first install, and the MCP registry guide covers where these servers are published.
Why compose multiple legal MCP servers
Take one question that crosses a border. A California engineer is moving to a Toronto subsidiary, and someone asks whether his non-compete will hold. The answer needs California law, Ontario law, and the cases behind each.
A single general-purpose model answering from memory can blend the two systems. California voids employee non-competes under Business and Professions Code section 16600, and the California Supreme Court read that section broadly in Edwards v. Arthur Andersen LLP, 44 Cal. 4th 937 (2008). Ontario handles the same question differently. Since October 25, 2021, section 67.2 (Part XV.1) of its Employment Standards Act, 2000 has barred employers from entering into non-competes with employees, with exceptions for executives and certain sales of a business (Ontario, Your guide to the Employment Standards Act). Covenants outside that ban are tested at common law, where the Supreme Court of Canada's decision in Shafron v. KRG Insurance Brokers (Western) Inc., 2009 SCC 6, treats an ambiguous restrictive covenant in an employment contract as prima facie unreasonable and unenforceable, and holds that a court cannot cure a defective covenant by notional severance, meaning reading it down to what the court thinks reasonable (SCC decision).
No single legal API covers all of that. CourtListener serves US court opinions, dockets, oral arguments and judges. CanLII covers Canadian courts, tribunals and legislation, and stops at the border. A statutes server serves US primary law by citation. If your matter has a cross-border choice-of-law clause, you need at least two of them. Before MCP the answer was glue code (you write an orchestrator and maintain it) or context-stuffing, and both get awkward on the first follow-up question.
MCP changes the shape. Each provider ships a server, your editor connects to all of them, and the model treats the tools as one toolbox. The only piece of code you write is the system prompt that tells the model which server to open first.
The three servers at a glance
| Server | What it covers | Cost | Auth |
|---|---|---|---|
| CourtListener (Vaquill-hosted) | US federal and state opinions, dockets, RECAP filings, oral argument audio, judges, citation lookup | Your own CourtListener account; free accounts have low API limits | Your CourtListener token, sent as a header |
| CanLII (Vaquill-hosted) | Canadian federal, provincial and territorial case metadata and citation links, plus legislation metadata, in English and French | Key by application to CanLII | Your CanLII key, sent as a header |
| Statutes server | US Code, CFR, federal and state court rules, state statutes and regulations across 56 US jurisdictions (50 states, DC and five territories), constitutions, Executive Orders since 2015 | Commercial API key | Your Vaquill AI API key |
A few details that matter when you compose them.

CourtListener is run by the nonprofit Free Law Project, which says its collection holds more than nine million decisions from more than 2,000 courts (Free Law Project, 7 May 2026). For a multiple-MCP-servers legal research setup it is the case-law lane: any time the question touches a US case name, a reporter cite, a docket, or a judge, the model should route there.
Free Law Project also runs its own official CourtListener MCP server at https://mcp.courtlistener.com/, which signs you in with OAuth instead of a pasted token. The CourtListener MCP guide covers it. Either server fits this workflow. The Vaquill-hosted one used below takes a header token instead of an OAuth sign-in. CourtListener itself holds no regulations, but the hosted build also bundles ecfr_* tools that read ecfr.gov, which says it is not an official legal edition of the CFR. This post sends regulation questions to the statutes lane instead, so the router prompt below names that lane for them.
One constraint to design around: free CourtListener accounts get low API limits. At the time of writing, Free Law Project's membership page lists 5 requests per minute, 50 per hour and 125 per day for a free account (Free Law Project membership). An agent in an exploratory loop will reach that quickly. Budget for it in the router prompt, not after the failures.
CanLII is a non-profit founded in 2001 by the Federation of Law Societies of Canada (CanLII, What is CanLII). Its API returns metadata, not full document text. You can ask the assistant to list the cases a decision cites and get a clean answer; you cannot ask it to summarize the reasoning in paragraph 47 and expect the MCP to return text. For full text you fall back to the CanLII website. The hosted CanLII server throttles to one request at a time, two per second and 5,000 per day for each key, and its README says these mirror CanLII's own per-key limits (README). CanLII's public API documentation does not list limits, so confirm them with CanLII when your key arrives. An agent that expands a citation graph two hops out can use a surprising share of a day's budget.
The US-primary-law server is the commercial piece in this stack. It is built for statutes and regulations by citation, which is the lane where an agent most needs the exact text. Case law in this stack comes from CourtListener. The point of a dedicated lane for statutes is the difference between an agent answering "what does 17 CFR 240.10b-5 say verbatim" from the published text and one improvising from memory.
One config, three servers
Drop this into Cursor's ~/.cursor/mcp.json. Cursor's documentation shows remote servers as a url with optional headers (Cursor Docs). VS Code uses its own .vscode/mcp.json file, with the servers under a servers key and a "type": "http" on each remote one (VS Code docs). Claude Code and Claude Desktop take the same inputs through the routes described after the block.
{
"mcpServers": {
"courtlistener": {
"url": "https://courtlistener-mcp.vaquill.ai/mcp/",
"headers": {
"X-CourtListener-Token": "YOUR_COURTLISTENER_TOKEN"
}
},
"canlii": {
"url": "https://canlii-mcp.vaquill.ai/mcp",
"headers": {
"X-CanLII-Token": "YOUR_CANLII_API_KEY"
}
},
"statutes": {
"command": "uvx",
"args": ["vaquill-mcp"],
"env": {
"VAQUILL_API_KEY": "vq_key_your_key_here"
}
}
}
}
Three servers, three keys, one restart of the editor. That is the whole installation.
Both hosted endpoints use streamable HTTP. On 12 October 2026 the CourtListener endpoint answered an MCP initialize request, and the CanLII endpoint answered it once a key header was present. Sent with no key, or with an Authorization: Bearer header instead, the CanLII endpoint refused the request with HTTP 401 and a message naming the X-CanLII-Token header and a ?token= URL parameter as the two ways to supply a key. The CourtListener server accepts X-CourtListener-Token per its README. The servers forward your key to CourtListener or CanLII and, per their READMEs, do not store it. The statutes server runs locally via uvx because it ships as a Python package, so the API key stays in your own environment.
Claude Code takes all three from the command line and needs the transport named for the hosted ones:
claude mcp add --transport http --scope user courtlistener \
https://courtlistener-mcp.vaquill.ai/mcp/ \
--header "X-CourtListener-Token: YOUR_COURTLISTENER_TOKEN"
claude mcp add --transport http --scope user canlii \
https://canlii-mcp.vaquill.ai/mcp \
--header "X-CanLII-Token: YOUR_CANLII_API_KEY"
claude mcp add --scope user statutes \
-e VAQUILL_API_KEY=vq_key_your_key_here \
-- uvx vaquill-mcp
--header and -e accept several values, so put them after the server name and URL. Put them first and the name and URL are read as more values for the flag. These commands follow claude mcp add --help and Claude Code's MCP documentation; they were not run against any account.
The router prompt
The model does not automatically know that "non-compete in Ontario" goes to CanLII and "17 CFR 240.10b-5" goes to the statutes server. Without a router prompt it has to infer the lane from tool descriptions alone, which is where mistakes creep in once you stack three servers.
The fix is a short system prompt that names the lanes and the data each covers. This one is a starting point. It goes in the Claude project instructions, a Cursor rules file, or the equivalent.
You have three legal MCP servers connected. Use them as follows.
JURISDICTION ROUTING
- US case law, dockets, PACER filings, judges, oral argument audio:
ALWAYS use the courtlistener server.
- Canadian decisions, Canadian federal/provincial legislation metadata:
ALWAYS use the canlii server. Note that CanLII returns metadata only;
for full text, point the user at canlii.org.
- US Constitution, US Code (USC), Code of Federal Regulations (CFR),
Federal Rules and state court rules, state statutes, state
regulations and constitutions, Executive Orders since 2015:
ALWAYS use the statutes server.
COMPOSITION RULES
- If the question spans multiple jurisdictions or data lanes, call all
relevant servers IN PARALLEL where independent, then synthesize.
- Never substitute a guessed citation when a tool call would resolve it.
- When you cite a US case, confirm the citation with the courtlistener
citation_lookup_citation tool before including it in output. A match
means the case exists, not that it supports the point or is still
good law.
- When you cite a US statute or regulation, retrieve the section text
before paraphrasing it.
CONSTRAINTS
- CanLII is rate limited per key. Prefer get_case over list_cases for
targeted questions; only browse a court's catalog if the user asks.
- CourtListener free accounts have low limits. Batch citation lookups
with citation_batch_lookup_citations when you have more than two.
- If you are unsure which server applies, ask the user one clarifying
question rather than guessing.
A few notes on shape.
The lanes are stated in terms of data the user might mention, not in terms of MCP tool names. A model routes "Ontario" to a server more easily than it routes "a question that could be answered by list_legislation". Tool names go in the prompt for the verification rules, where the call site matters, not for the routing rules, where the user's words matter.
Parallel calls are explicit because a client will not always send independent calls together unless told it may. Three sequential round trips feel slow even when each call is fast.
The "ask one clarifying question" line is the one engineers most want to delete. Keep it. The router cannot anticipate every legal phrasing, and one clarifying question is cheaper than five wrong tool calls against a rate-limited key.
For quick reference, here is the routing table in one shot:
| User utterance contains | Route to | Verification action |
|---|---|---|
| US case name, reporter cite, docket, judge, PACER | courtlistener | citation_lookup_citation before reasoning |
| USC, CFR, state code, federal rule, EO | statutes server | resolve_statute_citation, then get_us_statute_section_text |
| Canadian province, SCC, FCA, neutral cite | canlii | get_case for metadata, send user to canlii.org for text |
| Two or more of the above | all relevant, in parallel | verify each lane independently |
The tool names above are the ones the servers list. The CourtListener and CanLII lists were read from their live endpoints on 12 October 2026, and the statutes names from the Vaquill AI MCP tool list.
Three worked examples
The examples below show which tool each lane calls and what you can confirm afterward. They were not run end to end, because that needs your own CourtListener and CanLII keys. Argument names come from each server's published tool schema.
Example 1: cross-border non-compete enforceability
Prompt: "Compare non-compete enforceability for a senior engineer leaving for a competitor: California (US) vs Ontario (Canada). I need controlling authority on both sides."
The router fires three lanes in parallel:
1. statutes.resolve_statute_citation
{ cite: "Cal. Bus. & Prof. Code 16600" }
-> STATE_CA_Cbpc_D7_P2_C1_S16600, then get_us_statute_section_text
2. courtlistener.search_opinions
{ q: "Edwards v. Arthur Andersen", court: "cal" }
-> Edwards v. Arthur Andersen LLP, 44 Cal. 4th 937 (2008)
3. canlii.get_case
{ databaseId: "csc-scc", caseId: "2009scc6" }
-> metadata for Shafron v. KRG Insurance Brokers (Western) Inc., 2009 SCC 6
Item 1 was resolved against the live API on 12 October 2026. Section 16600(a) reads: "Except as provided in this chapter, every contract by which anyone is restrained from engaging in a lawful profession, trade, or business of any kind is to that extent void." Subdivision (b) tells courts to read the section broadly "in accordance with Edwards v. Arthur Andersen LLP (2008) 44 Cal.4th 937". The court value cal is CourtListener's id for the California Supreme Court. The databaseId and caseId formats in item 3 follow the examples in the CanLII server's tool schema, so check them against list_case_databases before relying on them.
A good synthesis then sets the California rule next to the Ontario rule, and reads Ontario's Employment Standards Act, 2000, Part XV.1, on the Ontario government's e-laws site, since CanLII's API gives metadata and links for legislation and not its text. The authorities named here were each checked against a live tool result or an official page when this post was reviewed. The lane layout does the useful work: a model that only searched case law could reach for Shafron and miss the statute that sits in front of it.
Example 2: regulation plus interpretive case law
Prompt: "What does 17 CFR 240.10b-5 say, and what Supreme Court cases interpret it?"
Two servers, no Canadian content. The router fires:
1. statutes.get_us_statute_section_text
{ act_id: "CFR_T17_P240_S240_10b_5" }
-> full text of the regulation
2. courtlistener.search_opinions
{ q: "10b-5 securities fraud scienter reliance", court: "scotus",
order_by: "score desc" }
-> ranked Supreme Court opinions to read
"17 CFR 240.10b-5" resolved to that act id and an eCFR source link on 12 October 2026. Search results will vary with the query, so read what comes back instead of assuming a list. Familiar results include Basic Inc. v. Levinson, 485 U.S. 224 (1988), and Dura Pharmaceuticals, Inc. v. Broudo, 544 U.S. 336 (2005), both of which CourtListener returns for those citations.
What matters is what does not happen. The model does not call CanLII at all, because the router prompt says Canadian law is out of scope unless the user mentions it. That keeps the CanLII daily budget for questions that need it.
Example 3: cite-checking a brief
Prompt: "Cite-check this draft motion to dismiss. Flag any case that does not resolve, any statute that does not exist, and anything where the cited proposition does not match what the court actually held." (The user pastes a ten-page brief with dozens of citations.)
This is where composition pays for itself. Each lane checks its own kind of citation:
1. courtlistener.citation_extract_citations_from_text
{ text: "<the brief>" }
-> the citations found in the text
2. courtlistener.citation_batch_lookup_citations
{ citations: [<the case citations>] }
-> a match, or no match, for each (up to 100 per call)
3. For the statute and regulation citations:
statutes.resolve_statute_citations_batch
-> the exact section for each, or unresolved
4. For the cases the argument leans on most:
courtlistener.get_opinion
-> read the opinion to check the cited proposition
Treat a citation that fails to resolve as unverified, not as fake. It might be a typo, a transposed volume, a slip opinion replaced by a published version, or an invented case, and only a person checking the court's own site can tell which. Treat a citation that resolves as proof the case exists, nothing more.
Budgeting calls
Each question can fan out to several calls, and the limits sit at different places. A free CourtListener account allows 125 requests per day, according to Free Law Project's membership page. The hosted CanLII server stops at 5,000 per key per day. On a free CourtListener account the CourtListener lane is therefore the one that runs out first. To find your own numbers, run one representative question a few times, count the tool calls, and multiply by the questions you expect per day.
If a single question routinely fires many calls, tighten the router prompt. Verify on output, not on every intermediate hop. Most clients also let you cap tool calls per turn, which stops a runaway loop before it drains a quota.
Gotchas
Rate limits add up. CourtListener's free tier is low and CanLII is capped per key. An agent in an exploratory loop is exactly the usage pattern that burns both. Put a cap on tool calls where your client allows one, and avoid citation-graph expansion two hops out unless the question needs it.
BYOK key management. Three keys means three rotation schedules and three audit trails. For a solo user, a .env and a regular rotation is fine. For anything multi-seat, run the keys through a vault and inject them at editor start. Free Law Project's MCP documentation warns that a CourtListener API token grants full access to your account's API surface, so treat it like a password.
Verification is still on you. MCP wires the model to real data. It does not remove the step between the data and the output. The model can still misread a case, misquote a statute, or paraphrase in a way that flips the meaning. The cite-checking pattern above is a partial answer: resolve every citation, retrieve the section text for every statute claim, and have a person read anything that goes out the door.
The hard part is not wiring the servers. It is the verification, key management, and rate-limit budgeting on top of whatever the model returns.
Tool-call order matters. If the response feels slow on a three-server question, check whether the client is calling the servers one after another, and whether the router prompt tells it independent calls may go together.
Canadian metadata is metadata. The CanLII MCP returns titles, citations, dates, keywords, and the citation graph. It does not return full opinion text. For text, the model has to send the user to canlii.org. Put that fallback in the router prompt or the model may improvise summaries from metadata alone, which is bad.
Troubleshooting in five steps
When the workflow misbehaves, check in this order.
- Slow responses. Confirm the router prompt tells the model it may call independent servers in parallel.
- Runaway tool calls. Cap tool calls per turn if your client supports it, or cap the agent's loop by turn count.
- Unresolved citations. Switch from individual
citation_lookup_citationcalls tocitation_batch_lookup_citations, which takes up to 100 citations per call. - Empty CanLII results. Check the
databaseId. A tribunal database is easy to pick when you wanted a Court of Appeal. Force alist_case_databasescall first when unsure. - Auth failures. Confirm header names:
X-CourtListener-Tokenfor the hosted CourtListener server andX-CanLII-Tokenfor the hosted CanLII server. They are not interchangeable with each other or with anAuthorizationheader. On claude.ai, remember that custom header names need Anthropic's approval.
FAQ
What does it mean to compose legal MCPs?
Composing legal MCPs means connecting several legal MCP servers to one AI client at the same time and letting the model treat their combined tools as a single toolbox. You add each server to one mcpServers config block, add a short router prompt so the model knows which server owns which jurisdiction, and ask one question that may touch all of them. The client routes each tool call to the right server behind the scenes.
How do I run multiple MCP servers for legal research at once?
In Cursor or VS Code, add each server as its own entry in the client's config (hosted servers use a url plus headers, local servers use a command), supply each server's key, and restart the client. Claude Desktop is the exception: claude_desktop_config.json holds local servers, so hosted servers go in through Customize > Connectors > Add custom connector instead. Claude Code takes all of them from claude mcp add, with --transport http on the hosted ones. Each extra server adds tools to the model's menu, so keep the active list to what the workflow needs.
How does the client decide which MCP server to call?
The model picks the tool from the merged catalog based on the tool descriptions and your prompt, then the MCP client routes that call to the server that owns the tool. A short router system prompt makes the choice more reliable by mapping plain-language cues (a Canadian province, a US reporter cite, a CFR section) to the right server.
Can one prompt query US and Canadian law together?
Yes. With CourtListener, CanLII, and a US statutes server connected, a single cross-border question (for example, non-compete enforceability in California versus Ontario) can fire calls to all three lanes and return one synthesized answer. The router prompt should tell the model to call relevant servers in parallel and verify each jurisdiction's authority independently.
Does the Vaquill AI API cover case law?
The Vaquill AI API is built for US primary law by citation: the US Code, CFR, court rules, state statutes and regulations, constitutions, and Executive Orders since 2015. In this stack, US case law comes through the CourtListener MCP, which is a separate server.
How do I keep an MCP agent from blowing through rate limits?
Cap tool calls per turn where your client allows it, prefer targeted lookups over catalog browsing, and verify citations on the final output rather than on every intermediate hop. Know your limits: a free CourtListener account allows 125 requests per day, and the hosted CanLII server stops at 5,000 per key per day. To size a workflow, run one representative question a few times and count the calls.
Which clients support multiple MCP servers?
Claude Desktop, Claude Code, Cursor and VS Code all let you connect more than one MCP server, but each has its own config location and format. Hosted servers connect over streamable HTTP with a url and headers; local servers launch with a command. After editing a config file, fully quit and reopen the client so it reloads the server list.
What this composes to
Three MCP servers, one config block, one router prompt, and you have an AI legal research environment that handles US case law, US primary law, and Canadian decisions and statutes in one chat thread, with parallel tool calls and a verification path you can defend.
Most of the value is in the routing. Each server is a clean data lane, and the router prompt is what turns three lanes into one cross-jurisdiction answer.
Vaquill AI publishes the statutes MCP, the CourtListener MCP, and the CanLII MCP as separate servers so you can compose them this way. For the US statutes, regulations and court rules in the statutes lane, see the US primary law API.
New legal AI guides, weekly.
Further Reading
Replace Harvey AI: Build Your Own Legal AI Stack on Claude + MCP (2026)
Read postLegal Research from Inside Cursor and Claude Code: The MCP Way
Read postCourtListener MCP Server: Setup Guide for US Legal Research
Read postGovInfo API vs a Legal Data API: What You Still Have to Build
Read postLegal Data Freshness: What Refresh Cadence Actually Means, Source by Source
Read postTop 12 Legal Redline Software Tools (2026)
Read post
Co-Founder & CTO
Priyansh leads engineering and AI at Vaquill AI: the pipelines that pull statutes, regulations and court rules from every US jurisdiction's official publisher, and the REST API, MCP server and open dataset that serve them.