MCP
Integrate Midpage's legal database with AI assistants using the Model Context Protocol.
Note: MCP supports court opinion research. Federal docket report and filing analysis tools are available in preview in v3. Statute and regulation tools are coming soon.
Versioning
These docs describe the current MCP contract, v3.
Midpage uses major versions for tool contract changes that integrations may need to account for, such as output schema changes or newly introduced tools. If your integration depends on fixed tool schemas or output fields, use a pinned version endpoint.
Previous version docs and migration notes:
- MCP v2:
https://app.midpage.ai/mcp/v2 - MCP v1:
https://app.midpage.ai/mcp/v1 - MCP changelog
Base URL
https://app.midpage.ai/mcp/v3
Use this pinned URL for the v3 contract documented here.
If you want your integration to automatically receive the latest tool implementations, use the unversioned URL instead:
https://app.midpage.ai/mcp
Authentication
Midpage supports two authentication models for MCP integrations.
| Option | Best for | How it works |
|---|---|---|
| API key | Server-side integrations, internal tools, and quick prototypes | Your integration sends one shared API key with each request. |
| OAuth | End-user apps, partner integrations, and multi-tenant products | Each user signs in with their own Midpage account and your client sends that user's access token. |
1. API Key
Use API key auth for non-interactive access, server-side jobs, or any integration that should run under one shared credential.
Generate an API key in the Developer Portal. If you need help, contact support@midpage.ai. Then send it in your MCP request headers as:
Authorization: Bearer <api_key>
2. OAuth
Use OAuth when your client should connect each user to their own Midpage account. Each user will need a Midpage account before they can sign in and authorize your client.
For most customers, the easiest setup is:
- Point your client at
https://app.midpage.ai/mcp/v3 - Let your MCP or OAuth library follow Midpage's discovery metadata and register a client automatically if it supports dynamic client registration
- Let the user sign in and approve access when prompted
Most MCP clients should not need pre-provisioned OAuth app credentials. Midpage's Clerk auth server supports dynamic client registration, so compatible clients can create their own credentials automatically. If your client cannot do that and needs a pre-provisioned OAuth app, contact Midpage.
Need the manual OAuth settings?
If your client supports discovery, start there. In most cases, pointing it at https://app.midpage.ai/mcp/v3 is enough. Midpage publishes protected-resource metadata on app.midpage.ai, and that metadata points clients to the Clerk authorization server. The settings below are only for cases where you need to configure the OAuth flow yourself.
Discovery
- MCP protected-resource metadata:
https://app.midpage.ai/.well-known/oauth-protected-resource/mcp - Clerk OAuth metadata:
https://clerk.midpage.ai/.well-known/oauth-authorization-server - Clerk OIDC discovery:
https://clerk.midpage.ai/.well-known/openid-configuration
Some older MCP clients expect OAuth authorization-server metadata on the same origin as the MCP server. Midpage currently publishes protected-resource metadata on app.midpage.ai and authorization-server metadata on the Clerk domain above, so older clients may need manual configuration using those Clerk URLs.
The current metadata advertises:
- authorization server:
https://clerk.midpage.ai - dynamic client registration endpoint:
https://clerk.midpage.ai/oauth/register - PKCE challenge method:
S256 - resource scopes:
profile,email
Which OAuth flow to use
- Public clients such as desktop apps, mobile apps, browser apps, and local CLIs should use Authorization Code + PKCE (
S256). Do not embed aclient_secretin those clients. If you need this mode, Midpage can provision your Clerk OAuth app as a public client. - Confidential server-side apps can use Authorization Code and exchange the code on their backend with either
client_secret_basicorclient_secret_post. - If you need refresh tokens for long-lived sessions, request
offline_access.
Minimal flow
- Fetch the protected-resource metadata document and read
authorization_servers. - Fetch the Clerk authorization-server or OIDC metadata document and use the published endpoints.
- Start an authorization request with
response_type=code,client_id,redirect_uri,state, and theresourcevalue from the protected-resource metadata document. - Request
profile emailfor MCP access. Addopenidif your client expects an ID token. Addoffline_accessif you need a refresh token. - Public clients must also send
code_challengeandcode_challenge_method=S256. - Exchange the authorization
codefor tokens at the Clerk token endpoint, again including the sameresourcevalue. - Call the MCP server with
Authorization: Bearer <access_token>.
Example authorize request for a public client
GET https://clerk.midpage.ai/oauth/authorize?
response_type=code&
client_id=YOUR_CLIENT_ID&
redirect_uri=https://your-app.com/oauth/callback&
resource=https%3A%2F%2Fapp.midpage.ai&
scope=profile%20email%20openid%20offline_access&
state=RANDOM_VALUE&
code_challenge=BASE64URL_SHA256(code_verifier)&
code_challenge_method=S256
Example token exchange for a public client
curl -X POST "https://clerk.midpage.ai/oauth/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code" \
-d "client_id=YOUR_CLIENT_ID" \
-d "code=AUTHORIZATION_CODE" \
-d "redirect_uri=https://your-app.com/oauth/callback" \
-d "resource=https://app.midpage.ai" \
-d "code_verifier=YOUR_CODE_VERIFIER"
Example token exchange for a confidential client
curl -X POST "https://clerk.midpage.ai/oauth/token" \
-u "YOUR_CLIENT_ID:YOUR_CLIENT_SECRET" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code" \
-d "code=AUTHORIZATION_CODE" \
-d "redirect_uri=https://your-app.com/oauth/callback" \
-d "resource=https://app.midpage.ai"
Calling the MCP server
Authorization: Bearer <access_token>
Testing in Hosted MCP Clients
If you just want to try Midpage in Claude, Cursor, or another hosted MCP client, add https://app.midpage.ai/mcp/v3 and sign in with your Midpage account when prompted.
You can create a free trial account at app.midpage.ai. For production integrations, use API key auth or OAuth as described above.
Available Tools
The latest MCP contract exposes:
searchfindInOpinionanalyzeOpinionanalyzeDocketReport(preview)analyzeDocketFiling(preview)
The docket tools are in preview. They are available for federal docket report and filing analysis, but their contracts may change as we refine docket retrieval workflows.
search
Search US case law across federal and state courts. Returns metadata, highlights, and citator treatment data.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
queries |
array | Yes | Array of search queries (1-4) run in parallel |
queries[].query |
string | Yes | Search query |
queries[].startDate |
string | No | Start date (inclusive, YYYY-MM-DD) |
queries[].endDate |
string | No | End date (inclusive, YYYY-MM-DD) |
queries[].jurisdictionType |
string | No | "federal", "state", or "state_and_federal" |
queries[].circuits |
string[] | No | Federal circuits: "1"-"11", "dc", "federal_circuit", "supreme_court" |
queries[].states |
string[] | No | State names (e.g., ["California", "New York"]) |
queries[].publishStatus |
string | No | "published", "unpublished", "unknown", "in_chambers", "separate", "errata", or "relating_to" |
queries[].courts |
string[] | No | Bluebook-style court abbreviations to narrow the search, such as ["S.D.N.Y.", "9th Cir."] |
If you filter on publishStatus, consider also running a parallel query with "unknown" because some jurisdictions have missing or uncertain publication metadata.
Example:
{
"queries": [
{
"query": "breach of fiduciary duty elements",
"jurisdictionType": "state_and_federal",
"states": ["California"],
"publishStatus": "published"
},
{
"query": "breach of fiduciary duty elements",
"jurisdictionType": "state_and_federal",
"states": ["California"],
"publishStatus": "unknown"
}
]
}
findInOpinion
Find quotable passages within a single opinion using keyword search.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
opinionId |
string | No* | Midpage document ID (e.g., "7228818") |
reporterCitation |
string | No* | Bluebook citation (e.g., "556 U.S. 662") |
docket |
object | No* | Court and docket number pair |
docket.courtAbbreviation |
string | Yes | Court abbreviation (e.g., "S.D.N.Y.", "9th Cir.") |
docket.docketNumber |
string | Yes | Docket number (e.g., "12-cv-20100") |
query |
string | Yes | Key terms to match |
* Provide one of opinionId, reporterCitation, or docket
Example:
{
"opinionId": "7228818",
"query": "minimum contacts purposeful availment"
}
analyzeOpinion
Reads the full document, then answers your question and returns the answer alongside text excerpts.
Accepts either opinionId, reporterCitation, or docket as input.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
opinionId |
string | No* | Midpage document ID |
reporterCitation |
string | No* | Bluebook citation (e.g., "556 U.S. 662") |
docket |
object | No* | Court and docket number pair |
docket.courtAbbreviation |
string | Yes | Court abbreviation (e.g., "S.D.N.Y.", "9th Cir.") |
docket.docketNumber |
string | Yes | Docket number (e.g., "12-cv-20100") |
question |
string | Yes | The legal question you want answered |
* Provide one of opinionId, reporterCitation, or docket
Example:
{
"reporterCitation": "556 U.S. 662",
"question": "What standard does this case establish for pleading requirements?"
}
Output fields:
| Field | Type | Description |
|---|---|---|
opinionId |
string | Midpage opinion ID |
question |
string | The question that was analyzed |
citation |
string | Bluebook citation for the opinion |
url |
string | General Midpage document URL |
supportedPropositions |
array | Propositions the opinion actually supports |
supportedPropositions[].proposition |
string | Cite-ready proposition supported by the quote |
supportedPropositions[].quote |
string | Verbatim supporting text from the opinion |
supportedPropositions[].scope |
string | Limits, conditions, or context for the proposition |
supportedPropositions[].centrality |
string | core_holding, supporting_analysis, secondary_matter, or background |
supportedPropositions[].opinionSection |
string | Optional source label for non-majority opinions, such as a concurrence or dissent |
supportedPropositions[].deeplinkURL |
string | Midpage URL linked to the supporting line range |
doesNotAddress |
string[] | Topics from the question the opinion does not address |
summary |
string | Short summary of what the case is about |
disposition |
string | Short procedural outcome phrase |
jurisdiction |
string | Court and jurisdiction |
treatment |
object | Citator treatment summary |
Example output:
{
"opinionId": "7228818",
"question": "What does this case hold?",
"citation": "Example v. Case, 1 F.4th 1 (9th Cir. 2024)",
"url": "https://app.midpage.ai/document/7228818",
"supportedPropositions": [
{
"proposition": "The court adopted the rule.",
"quote": "We adopt the rule.",
"scope": "Applies in the stated procedural posture.",
"centrality": "core_holding",
"opinionSection": "Smith, Concurring",
"deeplinkURL": "https://app.midpage.ai/document/7228818?lines=10-12"
}
],
"doesNotAddress": [],
"summary": "The court adopted a rule.",
"disposition": "affirming judgment",
"jurisdiction": "9th Circuit",
"treatment": {
"status": "positive",
"citedBy": 10
}
}
analyzeDocketReport (preview)
Analyze a federal docket report and answer a docket-level question. Use this for case snapshots, recent activity, procedural posture, service, complaint history, motion history, deadlines, and identifying filings that should be reviewed next.
This tool answers from the docket sheet. If the text of a specific filing is needed, use analyzeDocketFiling with the returned case.caseId and a relevantEntries[].filingId.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
court |
string | Yes | Federal court. Prefer Bluebook-style abbreviations such as "N.D. Cal.", "S.D.N.Y.", "D. Del.", "Bankr. D. Del.", "9th Cir.", or "Fed. Cir.". Full federal court names are also accepted. |
docketNumber |
string | No | Federal case docket number. Prefer full district or bankruptcy numbers with office or division prefix when known, such as "4:24-cv-04722"; appellate numbers look like "24-1234". |
caseName |
string | No | Optional case caption/name, such as "Musk v. Altman". Include it when available, especially if the docket number is incomplete. |
question |
string | Yes | The docket-level question to answer. |
Example:
{
"court": "N.D. Cal.",
"docketNumber": "4:24-cv-04722",
"caseName": "Musk v. Altman",
"question": "Give me a case snapshot and identify the operative complaint."
}
Output fields:
| Field | Type | Description |
|---|---|---|
status |
string | "ok" or a specific resolution/analysis error |
case |
object | Resolved case metadata |
case.caseId |
string | Case ID to use with analyzeDocketFiling |
case.caseName |
string | Resolved case caption, when available |
case.court |
string | Resolved court |
case.docketNumber |
string | Resolved docket number |
docketReport |
object | Docket report file metadata |
docketReport.url |
string | Downloadable Midpage file URL for the docket report |
docketReport.suggestedFileName |
string | Suggested filename if saving the docket report |
answer |
string | Direct answer based only on the docket sheet |
relevantEntries |
array | Docket entries or attachments supporting the answer or worth reviewing next |
relevantEntries[].filingId |
string | Filing ID to pass to analyzeDocketFiling, when available |
relevantEntries[].entryNumber |
string | Docket entry number |
relevantEntries[].attachmentNumber |
string | Attachment number, when the relevant filing is an attachment |
relevantEntries[].description |
string | Docket entry description, when available |
relevantEntries[].relevance |
string | Why the entry matters |
doesNotAddress |
string[] | Parts of the question the docket report does not answer |
warnings |
string[] | Optional warnings about report coverage or parsing |
analyzeDocketFiling (preview)
Analyze one docket filing selected from analyzeDocketReport and answer a focused question using passages from that filing. Use this when the user needs to understand what a specific filing says, not just what the docket entry says.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
caseId |
string | Yes | case.caseId returned by analyzeDocketReport |
filingId |
string | Yes | relevantEntries[].filingId returned by analyzeDocketReport |
question |
string | Yes | The question to answer about this specific filing |
Example:
{
"caseId": "00000000-0000-0000-0000-000000000000",
"filingId": "11111111-1111-1111-1111-111111111111",
"question": "What claims are asserted in this complaint?"
}
Output fields:
| Field | Type | Description |
|---|---|---|
status |
string | "ok" or a specific resolution/analysis error |
filing |
object | Resolved filing metadata |
filing.caseId |
string | Case ID |
filing.filingId |
string | Filing ID |
filing.entryNumber |
string | Docket entry number |
filing.dateFiled |
string | Filing date, when available |
filing.description |
string | Docket entry description for the filing |
filing.url |
string | Downloadable Midpage file URL for the filing |
filing.suggestedFileName |
string | Suggested filename if saving the filing |
answer |
string | Direct answer based only on the filing text |
supportingPassages |
array | Passages from the filing supporting the answer |
supportingPassages[].passage |
string | Supporting passage text |
supportingPassages[].relevance |
string | Why the passage matters |
doesNotAddress |
string[] | Parts of the question the filing text does not answer |
When citing filing content in a user-facing answer, hyperlink the citation to filing.url. Include the ECF number in the citation when available, for example: [Pl.'s Mot. for Summ. J. 5, ECF No. 30 (Mar. 3, 2020)](<filing.url>).
Changelog
See the MCP changelog for version history and migration notes.