Legal Research from Inside Cursor and Claude Code: The MCP Way

Short answer: To run legal research MCP in Cursor or Claude Code, add a legal MCP server to your config and point your assistant at it. For US statutes and regulations, add a statutes-and-regs MCP (for example, Vaquill AI's): run claude mcp add --transport http --scope user vaquill https://mcp.vaquill.ai/s/_ --header "Authorization: Bearer $VAQUILL_KEY" (Claude Code), or add the same URL and header to ~/.cursor/mcp.json for Cursor. Then ask the assistant something like "pull 17 CFR 240.10b-5," and it queries the live statutory corpus instead of guessing.

Reviewed and corrected on 12 October 2026. This is general information, not legal advice.

TL;DR

  • Claude Code, Cursor, and Claude Desktop all speak the Model Context Protocol, so the same legal MCP servers work in all three with small config differences.
  • Add a statutes-and-regs MCP for the US Code, CFR, and state statutes, the one slice the open ecosystem still does not normalize cleanly. Vaquill AI ships a hosted one at https://mcp.vaquill.ai/mcp (sign in with OAuth) or https://mcp.vaquill.ai/s/_ (API key in a header).
  • The hidden failure mode is rate limits: a wrapper that packages a 429 as a success-shaped response gives the model nothing real to lean on, and that silence is what invites a fabricated citation. Fix it with typed errors plus retry with full-jitter backoff.
  • Treat the setup as a credential-handling system: never commit raw keys, use ${env:VAR} interpolation, and run a gateway for shared firm deployments.
What would you do?
Question 1 of 4

Your editor's MCP tool panel shows a green checkmark for a citation lookup, but the server log shows the upstream returned a rate-limit error. The assistant has already written a confident holding. What went wrong?

Part of our MCP and developer guide series.

If you work mainly in Claude, How to Use Claude for US Legal Research (with MCP) covers that client in depth. For agent builders, see Adding Legal Research to Your AI Agent with MCP.

Why a silent tool failure produces a fake citation

Picture the setup: Cursor as the editor, Claude as the assistant, a legal MCP server wired in, and a prompt like "verify these three citations resolve to real sources and pull the text." The model returns a clean answer. Three citations, three holdings, three URLs.

Now suppose the third tool call hit a rate limit upstream (an HTTP 429), and the MCP wrapper packaged that 429 as a successful tool response with an error string inside. The tool panel shows a green checkmark. The server log tells a different story. The model, with no result to lean on, can do what models do when stranded mid-paragraph: produce a holding and a court that sound right.

The fix has two parts. The wrapper should retry the 429 with backoff. And when retries run out, it should surface a typed RateLimited error as a structured tool error, not a string. Models generally handle a structured error far better than an opaque blob that looks like a normal response: they can back off, ask the user, or decline the citation. If you are putting a wrapper into a lawyer's editor, check how it reports failure before anyone files anything.

That is the reason the retry section below exists.

The Statutes-and-Regs MCP for US Practice

No single MCP server covers the full primary-law surface a US lawyer touches in a day, and the hardest slice to wire up yourself is statutes and regulations.

A statutes-and-regs MCP covers the US Code, the CFR, and the statutes of the states. This is the piece that costs time because the open ecosystem still does not have a clean, normalized multi-state statutes corpus.

eCFR covers federal regs; state statutes are a mess of legislature websites with their own schemas and update cadences. A maintained wrapper here saves the integration tax. Vaquill AI ships a hosted one at https://mcp.vaquill.ai/mcp that covers the US Code, the CFR, the statutory codes of the 50 states, DC and the US territories, state regulations, court rules and agency guidance. Its tools include search_us_statutes, resolve_statute_citation, resolve_statute_citations_batch, get_us_statute_section, get_us_statute_section_text and get_section_changes. It is built for primary law retrieved by citation. For court opinions, add a separate server from a case-law provider.

Claude Desktop Config

Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS), %APPDATA%\Claude\claude_desktop_config.json (Windows), or ~/.config/Claude/claude_desktop_config.json (Linux). Settings > Developer > Edit Config opens it for you. This file takes stdio servers, so the statutes server goes in as its package:

{
  "mcpServers": {
    "vaquill": {
      "command": "uvx",
      "args": ["vaquill-mcp"],
      "env": {
        "VAQUILL_API_KEY": "YOUR_VAQUILL_KEY"
      }
    }
  }
}

The hosted remote server goes in as a connector instead of the package. Open Customize > Connectors > Add custom connector, enter the URL https://mcp.vaquill.ai/s/_, and add a request header called Authorization. Its value is Bearer YOUR_VAQUILL_KEY, typed exactly that way: Claude adds no scheme of its own, so do not repeat the header name inside the value. Request headers on a custom connector are a beta feature that Anthropic says is available to a limited set of organizations (Anthropic). If you do not see that section in the dialog, or would rather sign in than paste a key, use https://mcp.vaquill.ai/mcp and leave the header empty. On the Free plan you can add one custom connector.

Fully quit Claude Desktop and reopen it; closing the window is not enough, because the config is only read at launch. Then ask "what MCP tools are available?". You should see the statute-search and section-lookup tools listed. If you do not, check the MCP logs Claude Desktop writes (on macOS, under ~/Library/Logs/Claude). The usual suspects are a trailing comma in the JSON or a stale key.

Claude Code Config

Claude Code (the CLI / IDE harness) takes MCPs via the claude mcp add command. One line per server:

claude mcp add --transport http --scope user vaquill \
  https://mcp.vaquill.ai/s/_ \
  --header "Authorization: Bearer $VAQUILL_KEY"

Run claude mcp list to confirm it shows up and connects. Two traps. --header and -e take several values, so they come after the name and the URL; put them first and the URL can be swallowed into the option's value list. And the default scope is local, which registers a server only for the directory you ran the command in, so pass --scope user unless that is what you want. If a key shows up literally as $VAQUILL_KEY server-side, use double quotes and confirm with claude mcp list.

Prefer to avoid a key? Add https://mcp.vaquill.ai/mcp with --transport http and no header, then run /mcp and sign in.

Keep your keys in ~/.zshenv or a .envrc (with direnv), not in your shell history.

Cursor Config

Cursor reads MCP servers from ~/.cursor/mcp.json (global) or .cursor/mcp.json in the repo root (per-project). For a per-project setup that travels with the matter:

{
  "mcpServers": {
    "vaquill": {
      "url": "https://mcp.vaquill.ai/s/_",
      "headers": {
        "Authorization": "Bearer ${env:VAQUILL_KEY}"
      }
    }
  }
}

Cursor resolves ${env:NAME} in the command, args, env, url and headers fields (Cursor Docs). The syntax matters. Hard-coding a key into .cursor/mcp.json and then pushing the repo is the classic way a token ends up public on GitHub. We come back to this in the security section.

Then open Customize in Cursor's sidebar, find the server and make sure its toggle is on (Cursor Docs). If the server does not appear or load, restart Cursor.

Transport gotcha worth knowing

Every client names the transport differently, and one of them does not name it at all.

Cursor infers it. A remote server in ~/.cursor/mcp.json is a url plus optional headers, with no type key; a local one is a type: "stdio" plus a command. Cursor's documentation shows type as a required field only for stdio servers (Cursor Docs). You will find "type": "streamableHttp" in blog posts and README snippets. It is not in Cursor's documented remote schema, so give the server a url and let Cursor pick the transport.

Claude Code makes you say it. A hosted server needs --transport http on the claude mcp add command line. There is no type field to copy (Claude Code docs).

VS Code uses type, with its own vocabulary. .vscode/mcp.json takes "type": "http" or "type": "stdio" (VS Code docs). That is a third spelling of the same idea.

Claude Desktop's config file is for local servers. claude_desktop_config.json takes command / args / env entries. Hosted servers go in through Customize > Connectors > Add custom connector instead.

So the rule is: do not copy a server block between clients and expect it to survive. Rewrite it in the target client's shape, and check claude mcp list after every edit on the CLI.

A related trap shows up with stdio servers when uvx is not on the PATH your editor resolves. If a stdio server works from a terminal-launched editor but not from the dock, that is a common reason. Put the absolute path to uvx in the config, or launch the editor from a shell.

The 429 Pattern, Done Right

The failure above had one root cause: the wrapper did not handle 429 gracefully, and it did not tell the model what had failed. Both halves matter. The Vaquill AI API returns a 429 with a Retry-After header when you pass your plan's rate limit, and the retry loop below honors that header.

The retry loop, in TypeScript, looks like this. We ran it against a local test server that returned two 429 responses with Retry-After: 1 and then a 200: it succeeded on the third call after about two seconds, and a server that always returned 429 raised RateLimitedError when retries ran out.

type FetchOpts = { headers?: Record<string, string> };

async function fetchWithBackoff(
  url: string,
  opts: FetchOpts = {},
  maxRetries = 3,
): Promise<Response> {
  const baseMs = 500;
  const capMs = 8000;

  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    const res = await fetch(url, opts);

    if (res.status !== 429 && res.status !== 503) {
      return res;
    }
    if (attempt === maxRetries) {
      throw new RateLimitedError(`upstream ${res.status} after ${attempt} retries`);
    }

    // Honor Retry-After if the server sent one, else exponential backoff with full jitter.
    const retryAfter = Number(res.headers.get("retry-after"));
    const exp = Math.min(capMs, baseMs * 2 ** attempt);
    const jittered = Math.floor(Math.random() * exp);
    const sleepMs = Number.isFinite(retryAfter) && retryAfter > 0
      ? retryAfter * 1000
      : jittered;

    await new Promise((r) => setTimeout(r, sleepMs));
  }
  throw new RateLimitedError("unreachable");
}

class RateLimitedError extends Error {
  constructor(msg: string) { super(msg); this.name = "RateLimitedError"; }
}

Two details. First, full jitter (random between zero and the exponential ceiling) spreads retries from many clients so they do not hit the upstream in lockstep after a brief outage. The AWS Architecture Blog's Exponential Backoff And Jitter compares full, equal and decorrelated jitter and finds full jitter does less client work than equal jitter. Second, on exhaustion you throw a typed error so the MCP client sees a structured tool error, not a string blob.

The same discipline applies whatever the upstream is: a structured error on exhaustion is what lets you tell a client where every cite came from, instead of letting the model paper over a gap.

For multi-user deployments, do not retry from a single shared queue. Per-token queues prevent one heavy user from starving everyone else.

Three Workflows Worth Setting Up

Three prompts that use the statutes MCP directly:

1. Pull a specific regulation. When you need 17 CFR 240.10b-5:

"Pull 17 CFR 240.10b-5 and show me the statute it was issued under."

The assistant calls resolve_statute_citation, which returns the section with a link to the official source (for this one, the eCFR page on ecfr.gov), and get_us_statute_section_text for the text. We resolved this citation through the live API on 12 October 2026 and the response carried resolved: true, the eCFR link and the list of statutory authority for the part.

2. Cite-check a paragraph of statutes and regulations. Paste a paragraph and ask:

"Extract every statute and regulation citation, resolve each, and tell me which ones resolve and which do not."

The assistant can call resolve_statute_citations_batch. A citation the corpus does not hold comes back as resolved: false. In our 12 October 2026 test, 15 U.S.C. 9999 returned resolved: false with a reason, while 42 U.S.C. 1983 and 17 C.F.R. 240.10b-5 resolved. A miss means check it by hand, since it can mean a typo, a repealed section or a corpus gap. This step covers statutes and regulations only; case citations need a separate source.

3. Watch a body of law. Ask the assistant to set up a watch on a source, for example a state's statutes, with create_watch, so you are told when it changes.

None of the three require you to leave the editor. None require you to trust the model's memory for anything load-bearing.

Security Checklist

A legal MCP setup is a credential-handling system before it is anything else. If a key leaks, your firm's name is attached to whatever the abuser does next. Run this list before you deploy to anyone but yourself.

  • Never put a raw API key in a config file you commit to git. Use ${env:VAR} interpolation (Cursor) or environment variables sourced from a .envrc (Claude Code, Claude Desktop). Add .cursor/mcp.json to .gitignore if it has secrets in it at all. Better: keep the file template-only and load keys from the environment.
  • For shared firm deployments, run a gateway. Do not give every associate a copy of the same upstream API key. Run the MCP server behind a small gateway that holds the key, authenticates internal callers, and rate-limits per user. This also gives you the per-token queue from the previous section for free.
  • Rotate keys on a schedule. Put it on the calendar. For OAuth-based servers, revoke the authorization in your account instead of rotating a pasted token.
  • Audit-log every tool call, server-side. Tool name, caller identity, query parameters (redact PII), upstream status, latency. It proves to the partner-in-charge that you can answer "what did this assistant actually do."
  • Pin MCP server versions; never run latest. When you write uvx some-mcp you are pulling whatever was published most recently. Pin to a version (uvx some-mcp==1.4.2) and bump deliberately. A compromised dependency in an MCP server has direct access to the keys you handed it.
  • Disable destructive tools or gate them behind explicit confirmation. Some MCPs include delete_* or update_* tools that have no place in a research workflow. Read the tool list. Disable the ones you do not need.
  • Treat the LLM as untrusted on tool arguments. Validate inputs server-side. If a tool takes a citation string, validate the format before you call the upstream.

Most of those items get skipped because the setup starts as "just for me" and then five other lawyers in the firm ask for the same configs a month later. Plan for that on day one.

A Note on Trajectory

MCP puts the assistant inside the editor where lawyers already draft, and retrieval, synthesis and citation checks can happen against the same buffer. If you want to push this further, see how to use Claude for US legal research with MCP. You can have this workflow now with one hosted server, a config file, a real retry policy and the security checklist taped to the wall.

FAQ

Open ~/.cursor/mcp.json (global) or .cursor/mcp.json in the repo root (per-project) and add the server under mcpServers. For a statutes MCP like Vaquill AI's, add a "url": "https://mcp.vaquill.ai/s/_" entry and use headers with ${env:VAR} interpolation for the key. Save, open Customize in the sidebar, find the server and make sure its toggle is on, and restart Cursor if it does not load.

Use the claude mcp add command, one line per server. For US statutes and regs: claude mcp add --transport http --scope user vaquill https://mcp.vaquill.ai/s/_ --header "Authorization: Bearer $VAQUILL_KEY". Run claude mcp list to confirm it registered and connects, then ask the assistant a statutory question and it will call the tool. Claude Code names the transport with --transport http and has no type field; Cursor has no type on remote servers at all and infers the transport from the url. Do not copy a type value between the two.

What does the Vaquill AI statutes MCP cover?

It is a hosted remote MCP server at https://mcp.vaquill.ai/mcp (OAuth sign-in) or https://mcp.vaquill.ai/s/_ (API key in a header). It covers US primary law retrieved by citation: the US Code, the CFR, the statutory codes of the 50 states, DC and the US territories, state regulations, court rules, constitutions and agency guidance. For court opinions and dockets, use a separate case-law source.

It depends on the server. Key-based servers read a key you store as an environment variable and reference with ${env:VAR} rather than hard-coding it into a committed config. OAuth-based servers authorize in the browser instead.

Why does my AI assistant make up case citations?

A common cause is silent tool failure. When a legal MCP wrapper hits a rate limit (HTTP 429) and packages that failure as a success-shaped response, the model has no real result and can fill the gap with a holding that sounds right. Fix it by returning typed errors and retrying with full-jitter backoff so the model gets either a real record or a structured error it can decline on.

Yes. Edit ~/Library/Application Support/Claude/claude_desktop_config.json on macOS (or %APPDATA%\Claude\ on Windows, ~/.config/Claude/ on Linux), add the stdio server under mcpServers, then fully quit and reopen the app. A hosted remote server goes in a different way: Customize > Connectors > Add custom connector, with the key in a Request header.

Treat it as a credential-handling system. Never commit raw keys, keep config files template-only, run a gateway that holds the keys and rate-limits per user, audit-log every tool call, and pin server versions instead of running latest. Plan for the shared case on day one, because a "just for me" setup usually gets copied across the team within a month.


For the statutes-and-regs slot, Vaquill AI ships a hosted statutes-and-legislation MCP covering the US Code, the CFR and state codes. See the US primary law API for the full surface, or email contact@vaquill.ai.

Connect our US primary law database.
Every US statute, regulation, constitution, and executive order via REST, MCP or SQL. 5.4M+ sections, section-level citations, and links to the official source. Plus a free open dataset.
Updated October 12, 202618 min read

New legal AI guides, weekly.

Priyansh Khodiyar

Priyansh Khodiyar

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.