Skip to main content
Glama
smridhiwho

open-india-law-mcp

by smridhiwho

open-india-law-mcp

An MCP server that gives any MCP-compatible AI agent (Claude, Claude Code, Claude Desktop, or any other MCP client) structured, correctly-attributed access to Vaquill AI's Open India Law dataset:

  • 12.8M+ court judgments (Supreme Court + all 25 High Courts)

  • 813K+ tribunal/regulator matters

  • 1.1M+ legislation provisions (Central + every State/UT), section by section

It does not download the dataset. Court judgment files alone run to tens of GB per court, and the full corpus is ~53.6 GB of judgment text plus 463.6 GB of vector embeddings. Instead it uses DuckDB's hf:// support to run filtered SQL directly against the parquet files on the Hugging Face Hub, pulling only the row groups and columns a query actually needs.

Why this design ("accessing it rightly")

  • No hardcoded file list. The dataset can add or rename files between snapshots, so list_catalog asks the HF Hub what actually exists right now and categorizes it, instead of shipping a guess that goes stale.

  • No guessed schema for judgments. The README documents exact fields for legislation and tribunal records, but not for plain court judgments. search_judgments / get_judgment introspect the real schema at call time (get_schema) and pick the right columns, rather than assuming field names that might not exist.

  • CC BY 4.0 is honored automatically. Every tool response carries an attribution field (credit to Vaquill AI / Open India Law, CC BY 4.0) and a caveat field (point-in-time snapshot, not legal advice, verify against source_url/source_pdf_url). An agent doesn't have to remember the license terms — they travel with the data.

  • Scale-aware by construction. search_judgments requires a court (scanning all 26 courts in one call isn't something a single query should silently attempt), and every query tool caps results at 200 rows. The generic run_sql tool refuses anything that isn't a single read-only SELECT/WITH.

Related MCP server: pk-eli-mcp

Install

Requires Python 3.10+.

git clone <this-repo>  
cd open-india-law-mcp
pip install -e .

This pulls in mcp, duckdb, and huggingface_hub. The dataset is public, so no Hugging Face token is required — but if you hit HF rate limits, set HF_TOKEN in your environment and the server will use it.

Run it — 3 ways

Before any of these: run python smoke_test.py once. It hits the real Hugging Face API and DuckDB extension CDN and confirms the whole path works — nothing in this repo could be verified against live data during development (the build sandbox blocks both hosts), so this is the first real test.

1. As an MCP server (what Claude Desktop/Code actually run)

open-india-law-mcp
# equivalent: python -m open_india_law_mcp.server

This starts the stdio MCP server and blocks, waiting for a client to connect over stdin/stdout. You won't see output — that's normal; connect a client (below) rather than running it bare in a terminal to "test" it.

2. Directly from Python — no MCP protocol at all

@mcp.tool() registers a function with the server but returns it unchanged, so every tool is just a plain importable Python function. Skip the protocol entirely for a script, notebook, or test:

from open_india_law_mcp import server

catalog = server.list_catalog()
hits = server.search_legislation(query="arbitration", state="central", limit=5)
act = server.get_act(act_id="IND_central_1996_26")

Full runnable version: examples/direct_python.py.

3. As a real MCP client, in code

To drive it exactly the way Claude Desktop/Code do internally (spawn as a subprocess, speak MCP over stdio) — useful for testing, or as a template for a non-Claude agent:

import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

params = StdioServerParameters(command="open-india-law-mcp")

async def main():
    async with stdio_client(params) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()
            result = await session.call_tool("list_catalog", {})
            print(result.content[0].text)

asyncio.run(main())

Full runnable version: examples/mcp_client.py.

Connecting real MCP clients

Claude Code (CLI)

claude mcp add open-india-law -- open-india-law-mcp

Or, if it's not on your PATH:

claude mcp add open-india-law -- python -m open_india_law_mcp.server

Check it registered: claude mcp list. Remove it: claude mcp remove open-india-law.

Claude Desktop

Edit your config file directly — ~/Library/Application Support/Claude/claude_desktop_config.json on macOS, %APPDATA%\Claude\claude_desktop_config.json on Windows:

{
  "mcpServers": {
    "open-india-law": {
      "command": "open-india-law-mcp"
    }
  }
}

If you installed into a virtualenv rather than globally, either use that venv's full path for command (/path/to/venv/bin/open-india-law-mcp), or:

{
  "mcpServers": {
    "open-india-law": {
      "command": "python",
      "args": ["-m", "open_india_law_mcp.server"],
      "cwd": "/path/to/open-india-law-mcp"
    }
  }
}

Restart Claude Desktop after editing the config.

MCP Inspector (interactive debugging, no client needed)

npx @modelcontextprotocol/inspector open-india-law-mcp

Opens a browser UI where you can call each tool by hand and see the raw JSON — the fastest way to poke at this without writing any code.

Any other MCP client

This is a standard stdio server; point any MCP-compatible client at the open-india-law-mcp command the same way you would point it at any other.

Tools

Tool

What it does

list_catalog(refresh=False)

Lists every court/regulator/state file that currently exists, with its hf:// URI. Call this first.

get_schema(file_uri)

Real column names + types for a file, from list_catalog.

run_sql(sql, limit=20)

Read-only DuckDB SQL against any file(s) from the catalog. Full flexibility, capped at 200 rows.

search_legislation(query, state="central", act_id=None, in_force_only=True, limit=20)

Section-level search of one jurisdiction's Acts.

get_act(act_id, state=None)

Every section of one Act, in order. state is inferred from act_id if omitted.

search_regulations(body, query=None, in_force_only=True, limit=20)

Section-level search of one regulator's instruments (SEBI, RBI, ...).

search_judgments(query, court, year_from=None, year_to=None, limit=20)

Full-text search over one court's judgment chunks. court is required.

get_judgment(case_id, court)

Reassembles a full judgment from its chunks.

Plus a static resource, open-india-law://about, with the license and caveats in one place for clients that want to show it up front.

How DuckDB reads parquet straight off the Hugging Face Hub

Nothing here is server-specific — it's plain DuckDB, so you can reproduce any query from the duckdb CLI with zero Python:

duckdb -c "
INSTALL httpfs; LOAD httpfs;
SELECT act_id, section_number, section_title
FROM read_parquet('hf://datasets/vaquill/open-india-law/in_central_legislation.parquet')
WHERE text ILIKE '%arbitration%'
LIMIT 5;
"

The hf:// URI. DuckDB's httpfs extension recognizes hf://datasets/<repo>/<path> and resolves it to the Hub's actual CDN/resolve URL under the hood — you never see or construct that URL yourself. catalog.hf_uri() in this repo just builds the hf:// string; DuckDB does the resolution.

Why this doesn't download whole files. Parquet stores its schema and per-row-group statistics (min/max, null counts) in a footer at the end of the file. httpfs does an HTTP range request for that footer first, then — for each column/predicate in your query — issues further range requests only for the row groups whose stats can't rule them out. A WHERE year = 2023 on a 2 GB file might read a few MB. This is exactly what lets search_legislation/search_judgments filter multi-GB files in place instead of streaming them wholesale.

Free-text search doesn't get this for free. ILIKE '%arbitration%' has no column statistic that can prove a row group doesn't contain the word — DuckDB has to actually read every row group it can't rule out by some other predicate. That's why search_judgments requires a court (one file, not 26) and accepts year_from/year_to: the year filter prunes row groups first, and only the survivors get scanned for text.

Caching within a connection. duck.py enables enable_object_cache (keeps parsed parquet footers in memory) and enable_http_metadata_cache (keeps ETag/size lookups in memory) on every connection it creates. Since each worker thread reuses one long-lived connection, calling get_schema then search_legislation against the same file only fetches that footer once. There's no cross-process or on-disk cache — a fresh server process starts cold.

Authentication. The dataset is public, so no token is required. If you hit Hub rate limits, set HF_TOKEN in the environment the server runs in — duck.py picks it up and registers it as a DuckDB secret (CREATE SECRET hf_token (TYPE HUGGINGFACE, TOKEN ...)) automatically.

Discovery vs. querying are two different Hub calls. list_catalog uses huggingface_hub.HfApi.list_repo_files (a small metadata API call, cached for 15 minutes in catalog.py) to find out what files exist at all. Actually reading a file's contents is a separate, unrelated path — DuckDB's own HTTP client via httpfs, not huggingface_hub. The Python package huggingface_hub is only ever used for that one discovery call.

Performance notes

  • Narrow before you search: pick one court/state/regulator file, and add a year range when the tool supports it. That's the difference between a sub-second query and one that has to scan a large fraction of a multi-GB file.

  • Tribunal decisions (CAT, ITAT, NCLT, ...) are ~2.1M scanned PDFs with no text layer per the source dataset — they aren't in the judgment parquet files and this server doesn't attempt OCR. Use list_catalog / run_sql against whatever tribunal metadata files do exist in the repo.

License

This server's code: MIT (add your own LICENSE file if you want one). The data it queries is CC BY 4.0 — see attribution.py and the attribution/caveat fields returned by every tool. The underlying legal text is Government of India material reproduced under s.52(1)(q) of the Copyright Act 1957. None of this is legal advice.

A
license - permissive license
A
quality
C
maintenance

Maintenance

UpdatingMaintainers
UpdatingResponse time
Release cycle
0Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables semantic search over Polish court judgments and legislative acts via MCP. Allows LLMs to retrieve legal documents using natural language queries.
    Apache 2.0
  • A
    license
    A
    quality
    D
    maintenance
    Connects LLMs to the EcourtsIndia Partner API for searching Indian court cases, retrieving orders, reading cause lists, and accessing AI summaries.
    9
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    Grounds external agents in public Indian judgments and statutes through MCP, enabling legal search and bounded document packets without requiring the full Roop platform.
    1
    MIT

View all related MCP servers

Related MCP Connectors

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/smridhiwho/india-law-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server