Skip to main content
Glama
README.md
# @courtmesh/mcp-server

An MCP (Model Context Protocol) server for the CourtMesh public REST API. It gives any MCP capable AI client, Claude Desktop, Claude Code, 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](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:

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

## 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 Claude Desktop, Claude Code and Cursor. 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` | No | Reads any existing AI analysis for a case, read only. |
| `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 (20 credits, 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. |

18 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`.

### 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

### Claude Desktop

Edit `claude_desktop_config.json` (Settings, Developer, Edit Config) and add:

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

### Claude Code

CLI one liner:

```bash
claude mcp add courtmesh --env COURTMESH_API_KEY=cm-your-key-here -- npx -y @courtmesh/mcp-server
```

Or add to `.mcp.json` in your project:

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

### Cursor

Edit `~/.cursor/mcp.json`:

```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:

```json
{
  "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:

```json
{
  "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

```bash
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`.

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.

Maintenance

ActivityMaintained
ResponsivenessNo issues