Skip to main content
Glama

@courtmesh/mcp-server

An MCP (Model Context Protocol) server for the CourtMesh public REST API. It gives any MCP capable AI client, Cursor, or a custom agent, tools to search and analyze Indian court case law, screen a party name for litigation history, and check corpus coverage: 310M plus case records spanning the Supreme Court, High Courts, District Courts and tribunals.

Built with the official @modelcontextprotocol/sdk. MIT licensed.

Quickstart

1. Get an API key

Sign up and generate a key at https://research.courtmesh.ai. Keys look like cm-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-xxxx (a legacy vv- prefix also works).

2. Run it

You do not need to install anything by hand, npx will fetch and run the package. Set COURTMESH_API_KEY in your MCP client config, see the copy pasteable blocks below.

Local checkout:

npm install
npm run build
COURTMESH_API_KEY=cm-your-key-here node dist/index.js

Related MCP server: canlii-mcp

Environment variables

Variable

Required

Default

Description

COURTMESH_API_KEY

Recommended

none

Your CourtMesh API key. Tools list fine without it, but any real API call will fail with a 401 style error until it is set. In HTTP mode a per session ?token= query parameter overrides this.

COURTMESH_API_BASE_URL

No

https://research.courtmesh.ai/api/v1/prod

Override to point at a different CourtMesh environment.

MCP_TRANSPORT

No

stdio

Set to http to run the Streamable HTTP transport instead of stdio. Equivalent to passing --http.

PORT

No

3000

Port for the HTTP transport.

COURTMESH_DEFAULT_TIMEOUT_MS

No

120000

Timeout for most tool calls.

COURTMESH_TIMELINE_TIMEOUT_MS

No

240000

Timeout for request_case_timeline, which can trigger a live court fetch.

COURTMESH_LONG_TIMEOUT_MS

No

630000

Timeout for semantic_search_cases and analyze_consolidated_case, which can take minutes.

Transports

This server supports two transports, chosen at startup:

  • stdio (default): the standard transport for local MCP clients such as Cursor and other desktop and command line MCP clients. The client spawns the server process and talks to it over stdin and stdout.

  • Streamable HTTP: pass --http or set MCP_TRANSPORT=http to run as a long lived HTTP server, mounted at /mcp, suitable for hosting behind a URL such as mcp.courtmesh.ai/mcp. A plain GET /health route is also available on the HTTP server itself, separate from the check_api_health tool, which checks the upstream CourtMesh API instead.

Tools

Tool

Endpoint

Credits

One line

search_indian_court_cases

POST /search/cases

No

Fast keyword and boolean search over the full 310M plus case index.

semantic_search_cases

POST /search/cases/semantic

Yes

AI vector search over the roughly 2M case subset with embeddings, for natural language questions about legal concepts.

get_case

GET /cases/{id}

No

Full case details, without AI analysis.

get_case_analysis

GET /cases/{id}/analysis

Yes

Reads any existing AI analysis for a case, read only. 1 credit per read; the first read of a case by your organization costs the credits generating that analysis used.

find_related_cases

GET /cases/{id}/related

No

Other documents sharing the same case number, plus a procedural timeline.

search_judges

GET /judges/search

No

Autocomplete over Supreme Court and High Court judge names.

analyze_case

POST /cases/{id}/analyze

Yes

Triggers AI analysis of one case, asynchronous. allowRemoteFetch: true permits fetching the source document from a remote court host, at a 20 credit surcharge.

analyze_consolidated_case

POST /cases/{id}/analyze-consolidated

Yes, more

AI analysis merged across every document sharing a case number, synchronous and slow.

get_case_pdf_url

GET /cases/{id}/pdf

No

A time limited, encrypted link to the official judgment PDF.

request_case_timeline

POST /request-timeline

No, unless refresh: true

Reads stored order and hearing history by default (1 credit); refresh: true forces a live fetch from the court's own systems (also 1 credit, PAYG tier or above).

get_case_timeline

GET /get-timeline/{requestId}

No

Polls the job started by request_case_timeline.

screen_party_litigation

POST /party/screen

Yes

Litigation check for a person or company name: KYC, BGV, due diligence, and litigation/compliance screening.

get_court_coverage

GET /coverage

No

Corpus coverage and freshness snapshot, no authentication required.

check_api_health

GET /health

No

Checks CourtMesh API connectivity, no authentication required.

get_api_usage

GET /usage

No

This key's tier, wallet balance, per period limits and per endpoint call volume for the current month.

list_reference_courts

GET /reference/courts

No

The court taxonomy accepted by court filters elsewhere, no authentication required.

list_reference_case_types

GET /reference/case-types

No

Every caseType value accepted elsewhere, no authentication required.

screen_party_litigation_batch

POST /party/screen/batch

Yes

Screens up to 25 names in one call, same check as screen_party_litigation; each item independently priced and independently able to fail. Not available on the Free tier, no adjudicate option.

lookup_case_by_cnr

GET /cases/by-cnr/{cnr}

1 credit when a record is returned

Finds one case from its 16 character CNR. fetch: true starts a live court fetch for a CNR not in our records yet: the starting call returns pending and is free, the call that returns the record is the 1 credit charge, and the fetch shares the daily live fetch cap. Call again with the same CNR to poll. If the court had nothing for the CNR in the last 24 hours it is not asked again and the call is a 404.

create_watchlist

POST /watchlists

No

Creates a watchlist and optionally starts watching up to 100 CNRs. Daily monitoring is included in your plan, within the watched case cap.

list_watchlists

GET /watchlists

No

Lists the account's watchlists.

add_cases_to_watchlist

POST /watchlists/{id}/items

No

Starts watching 1 to 100 CNRs, with a per case result (watching, pending, invalid, duplicate, cap_exceeded, not_found). cap_exceeded covers the watched case cap, a daily add allowance (Free 50, Pay As You Go 5,000, Scale 50,000, Enterprise 500,000) and the daily limit on CNRs we do not hold yet.

remove_case_from_watchlist

DELETE /watchlists/{id}/items/{cnr}

No

Stops watching one CNR (404 ITEM_NOT_FOUND if it is not on the list).

list_watchlist_cases

GET /watchlists/{id}/items

No

The cases on a watchlist with their current snapshot (status, stage, next hearing date, order count). A case taken down at a party's request shows as unavailable with its case fields null.

get_case_changes

GET /changes, GET /watchlists/{id}/events

1 credit per non-empty page

The change feed: new orders, hearing date, status, stage and disposal changes. An empty page is free, so polling is cheap.

25 tools in total. Full input and output field details are in each tool's own description, visible to any connected MCP client through tools/list.

Monitoring a set of cases

Ask the model to watch cases by CNR ("watch DLHC010084212021 and DLHC010084222021 for me"); it creates or reuses a watchlist with create_watchlist and add_cases_to_watchlist. Later, "what changed on my cases since yesterday" is answered by get_case_changes, which returns only new events when you carry its pagination.nextCursor forward. Webhook management is intentionally not exposed as a tool: webhooks send account data to a URL and their signing secret is shown once, so set them up in code with the Node or Python SDK instead.

The monitoring tools do not send an Idempotency-Key: adding a case that is already watched is simply reported as duplicate, so a retry is safe without one.

Idempotency

analyze_case, analyze_consolidated_case, request_case_timeline, screen_party_litigation and screen_party_litigation_batch each send an Idempotency-Key header automatically, derived as a SHA-256 hash of the tool name plus its exact arguments (see computeIdempotencyKey in src/tools.ts). If a model retries one of these calls with identical arguments, whether because of its own retry logic, a dropped connection, or a re-sent turn, the server recognises the replay (same key, same body) and returns the stored response again instead of re-running the job or re-charging credits. Changing even one argument produces a different key and runs as a brand new, separately charged call. This requires no configuration and needs no argument from the calling model.

When a call is served from that cache, the server answers with an Idempotency-Replayed: true header; client.ts turns that into a replayed: true field on the parsed response, and the tool result text for all five of these tools starts with a Replayed: identical request served from the 24 hour idempotency cache, no credits charged. line, so the calling model (and anyone reading a transcript) has a positive, explicit signal instead of having to notice on its own that, for example, screen_party_litigation redacted query.name/query.aliases on that response.

Configuration examples

Any stdio MCP client

Add this to your client's MCP server configuration (the file name and location vary by client; most use an mcpServers object):

{
  "mcpServers": {
    "courtmesh": {
      "command": "npx",
      "args": ["-y", "@courtmesh/mcp-server"],
      "env": {
        "COURTMESH_API_KEY": "cm-your-key-here"
      }
    }
  }
}

Cursor

Edit ~/.cursor/mcp.json:

{
  "mcpServers": {
    "courtmesh": {
      "command": "npx",
      "args": ["-y", "@courtmesh/mcp-server"],
      "env": {
        "COURTMESH_API_KEY": "cm-your-key-here"
      }
    }
  }
}

Hosted HTTP mode

https://mcp.courtmesh.ai/mcp is live: point any Streamable HTTP capable MCP client at it directly, no local process required. Send your key either as an Authorization: Bearer header (preferred) or as a ?token= query parameter, for clients that can only configure a URL; either form overrides COURTMESH_API_KEY for that session.

Header form:

{
  "mcpServers": {
    "courtmesh": {
      "url": "https://mcp.courtmesh.ai/mcp",
      "headers": {
        "Authorization": "Bearer cm-your-key-here"
      }
    }
  }
}

Query parameter form, for a client that cannot set headers:

{
  "mcpServers": {
    "courtmesh": {
      "url": "https://mcp.courtmesh.ai/mcp?token=cm-your-key-here"
    }
  }
}

Error codes

The API's own error text is always surfaced verbatim where available, along with a plain explanation.

Status

Meaning

What to do

401

The API key is missing, malformed, invalid, or deactivated.

Set COURTMESH_API_KEY to a valid key, or fix the ?token= value in HTTP mode. Get a key at https://research.courtmesh.ai.

402

Insufficient credits for the endpoint's price, on any priced call (analyze_case, analyze_consolidated_case, semantic_search_cases, screen_party_litigation, a live request_case_timeline refresh). The response includes required, balance and shortfall.

Buy a credit pack or upgrade the plan, then retry.

403

Account, plan, or quota gate, distinguished by a machine readable code: API_TIER_NOT_ALLOWED (a Free tier account calling analyze_case, analyze_consolidated_case, get_case_analysis, or adjudicate: true on screen_party_litigation, none of which the Free tier may use), SEMANTIC_NOT_ALLOWED (semantic_search_cases on the Free tier), LIVE_FETCH_NOT_ALLOWED (refresh: true on request_case_timeline below PAYG), REMOTE_FETCH_NOT_ALLOWED (analyze_case needs allowRemoteFetch: true, or the document lives outside the allowed host list), PARTY_SCREEN_LIMIT_REACHED (monthly screen cap), or an account level gate (deactivated, suspended, not found, credits exhausted).

Check your CourtMesh billing and plan settings, upgrade off the Free tier, pass the flag the error names, or wait for the quota to reset.

429

Rate limited, distinguished by code: RATE_LIMITED (a request or minute/day/month cap), DISTINCT_NAMES_LIMIT_REACHED or DISTINCT_CASES_LIMIT_REACHED (an additional abuse protection was exceeded), PDF_LIMIT_REACHED, LIVE_FETCH_LIMIT_REACHED, TOO_MANY_KEYS_FROM_IP, or CONCURRENT_ANALYSIS_LIMIT.

Wait the number of seconds given in retryAfter before retrying, the message also states when the limit resets. This client already retries a RATE_LIMITED 429 on a read only (GET) tool once by itself, honouring Retry-After capped at 60 seconds; a write (POST) tool such as screen_party_litigation or analyze_case is never retried automatically, so an error here on one of those means the call did not run and is safe to resend yourself.

400

Validation failed, or a request shape the plan does not allow: PAGE_LIMIT_EXCEEDED / PAGINATION_DEPTH_EXCEEDED (the tier's page size or pagination depth cap), CURSOR_INVALID (a cursor that is malformed, expired, or was issued for a different query).

For a plain validation failure, the tool result lists the specific field errors from the API's details array, fix the input and retry. For a cap or cursor problem, reduce page/limit, drop cursor and start the search again, or upgrade the plan.

404

The case, PDF, or timeline request was not found. get_case_pdf_url distinguishes CASE_NOT_FOUND (no such case) from PDF_NOT_STORED (case exists, no stored document; try request_case_timeline with refresh: true).

Double check the id or requestId.

408 / 500 / 502 / 503

Timeout or upstream failure.

Usually transient, retry later.

When an error body carries a requestId, this server appends it to the reported message so it can be handed to CourtMesh support.

Development

npm install
npm run build   # compiles TypeScript with tsc, then chmods dist/index.js executable
npm run dev      # tsc --watch
npm start        # runs the built server over stdio

Source layout:

  • src/index.ts: entry point, transport selection (stdio vs Streamable HTTP), Express app for HTTP mode.

  • src/client.ts: shared HTTP client, auth headers, and all error mapping.

  • src/tools.ts: tool schemas (zod) and handlers, one per CourtMesh endpoint.

  • src/context.ts: an AsyncLocalStorage used to carry a per request ?token= override through to the client in HTTP mode.

See CHANGELOG.md for what changed in each release.

License

MIT, Copyright 2026 Thinkscoop Technologies LLP. See LICENSE.

Available Tools

12 tools
analyze_caseAnalyze CaseA

Triggers AI analysis of a single case. CONSUMES AI CREDITS. ASYNCHRONOUS: normally returns immediately with status processing while the analysis runs in the background; poll get_case_analysis after roughly 30 to 60 seconds to retrieve the result. If analysis already exists and force is not set, the existing analysis is returned immediately instead with alreadyExists true. Set force true to re-analyze a case that already has analysis, which also consumes credits again. Fails with a clear message if the case has no usable text or PDF content to analyze.

Inputs: id (required), force.

analyze_consolidated_caseAnalyze Consolidated CaseA

Runs AI analysis across ALL documents that share the given case number, producing one merged view of the whole matter. CONSUMES SIGNIFICANTLY MORE AI CREDITS than analyze_case. SYNCHRONOUS: this call blocks until the analysis completes, which can take several minutes, so a long timeout is used. For a High Court case it analyzes the case document plus up to 5 most recent orders. For a Supreme Court case it analyzes up to 20 documents sharing the case number. Set force true to redo analysis that already exists, at the cost of credits again. Fails if the case has no case number or no text content, or if AI credits are exhausted.

Inputs: id (required), force.

check_api_healthCheck API HealthA

Checks whether the CourtMesh API is reachable and healthy. No authentication required and no AI credits consumed. Useful to verify connectivity, and to confirm that connectivity problems are not caused by API key configuration, since this endpoint works even without a key. Returns success, status, version and timestamp; note this endpoint does not use the standard data envelope used by every other tool here.

Inputs: no named parameters.

get_caseGet CaseA

Fetches full details for one case, without AI analysis. No AI credits consumed. Returns id, caseNumber, title, court, caseType, judges, petitioners, respondents, decisionDate, disposalNature, summary, optional metadata.diaryNumber, hasDocuments, documentCount and hasAnalysis. Fields like detailedSummary, headnote, holding and keyFacts are NOT included here, call get_case_analysis for those.

Inputs: id (required).

get_case_analysisGet Case AnalysisA

Reads any existing AI analysis for a case. Read only: does NOT trigger new analysis and does NOT consume AI credits. If analysis has not been generated yet, hasAnalysis will be false and you will get a message field instead of an analysis field; in that case call analyze_case to generate it. When hasAnalysis is true, analysis may include summary, detailedSummary, comprehensiveSummary, headnote, holding, keyFacts, issues, courtsReasoning, citedCases (followed, distinguished, overruled, referred), precedentRelationships, arguments (petitioner, respondent), practiceAreas, subCategories, tags, procedureType, precedentValue, legalPrinciples, doctrinesApplied, statutoryInterpretation and constitutionalProvisions. Fields with no value are omitted from the response.

Inputs: id (required).

get_case_pdf_urlGet Case PDF URLA

Returns a time limited link to the official judgment PDF for a case. No AI credits consumed. CRITICAL: the returned pdfUrl is an ENCRYPTED presigned S3 URL, not a directly fetchable link, it must be decrypted with a case specific key before use, and it expires after the returned expiresIn seconds (normally 3600). Do not attempt to fetch pdfUrl directly, treat it as an opaque token to hand back to the user or to a CourtMesh client that knows how to decrypt it. Returns 404 if the case has no stored document.

Inputs: id (required).

get_case_timelineGet Case TimelineA

Polls the status and result of a job started by request_case_timeline. No AI credits consumed. Returns requestId, status, createdAt, updatedAt plus, when available, startedAt, completedAt, error, result, orders, orderCount and totalOrderCount. Returns 404 if the request is not found, which can mean the requestId is wrong or has expired.

Inputs: requestId (required).

request_case_timelineRequest Case TimelineA

Kicks off a fetch of the live order and hearing history for a case directly from the court's own systems. Asynchronous job, no AI credits consumed. case_id must be the 24 character MongoDB ObjectId string, the id field from search results, a case number will fail. Supreme Court cases return immediately with status completed and orderCount 0, since SC cases have no separate order history in this system. District Court cases are fetched synchronously and come back completed or failed. High Court cases usually return pending and must be polled with get_case_timeline using the returned requestId.

Inputs: case_id (required).

search_indian_court_casesSearch Indian Court CasesA

Keyword and boolean search over the full 310M plus record OpenSearch index of Indian court cases. Fast, exact match, does not consume AI credits. Prefer this over semantic_search_cases for case numbers, party names, citations, judge names and exact phrases. Use semantic_search_cases instead when the request is a natural language question about legal concepts, doctrines or fact patterns rather than exact terms. Note: the caseNumber field is accepted and echoed back but does not actually filter results, put case number text in query instead. Note: sortBy only accepts relevance or date at the validation layer, but the underlying search engine only understands relevance, recent or oldest internally, so date is accepted yet may not reorder results as expected.

Inputs: query (required), page, year, court, +10 more.

search_judgesSearch JudgesA

Autocomplete over the combined Supreme Court and High Court judge name list. No AI credits consumed. Use this to get the exact spelling of a judge name before passing it to search_indian_court_cases as judgeName. Matching is case insensitive substring matching. An empty or omitted q returns the first 50 names in the list. Results are always capped at 50.

Inputs: q.

semantic_search_casesSemantic Search CasesA

AI vector search over the roughly 2M case subset that has embeddings, out of the full 310M plus corpus. CONSUMES AI CREDITS. Prefer this over search_indian_court_cases for natural language questions about legal concepts, fact patterns or doctrines, where exact keywords will not match. Slower, a single call can take a minute or more. Real behaviour note: this endpoint's validation layer also accepts top level court, year, caseType, caseNumber, judgeName, judges, judge, fromDate and toDate fields, but the handler silently ignores all of them, only query, page, limit and filters are actually used, so this tool only exposes those. Put any filtering inside the filters object instead. Another quirk: if the cleaned query text ends up shorter than 3 characters after internal processing, the service falls back to a plain keyword search and marks the response with meta.fallbackMode = "opensearch".

Inputs: query (required), page, limit, filters.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A4.6/5.0

    Scored across 12 tools

    Disambiguation5/5

    Each tool has a distinct purpose: get_case vs get_case_analysis clearly separate raw case data from AI analysis; analyze_case vs analyze_consolidated_case distinguish single vs multi-document analysis; search_indian_court_cases vs semantic_search_cases differentiate keyword vs semantic search; request_case_timeline and get_case_timeline form a clear start/poll pair. No two tools are likely to be confused.

    Naming Consistency4/5

    Most tools follow a verb_noun pattern with verbs like get, search, analyze, request, check. There is slight inconsistency: 'find_related_cases' could be 'search_related_cases' or 'get_related_cases', and 'request_case_timeline' uses request while its pair uses get. However, the naming is generally predictable and the intent is clear.

    Tool Count5/5

    12 tools is well within the ideal 3-15 range. Each tool covers a distinct aspect of legal case research (retrieval, analysis, search, timeline, PDF, health), and none feel redundant or unnecessary. The count matches the apparent scope of the server.

    Completeness5/5

    The tool surface provides full coverage for the domain: search cases, get case details, read and generate AI analysis (both single and consolidated), find related cases, retrieve PDFs, and fetch timelines. The addition of search_judges and health check round out the workflow. No major gaps are apparent for the stated purpose of accessing Indian court case data and analysis.

    Related MCP Connectors

    Related MCP Servers