Content
# Tool List
[English](https://github.com/lawchat-oss/mcp-taiwan-legal-db/blob/main/README.en.md) ·
Taiwan Regulations, Judgments, and Constitutional Court Decisions — MCP Server.
Let any MCP-compatible AI assistant directly access Taiwan's public legal data:
- **Judicial Yuan Judgments** — judgment.judicial.gov.tw (full-text search + retrieval)
- **National Regulation Database** — law.moj.gov.tw (11,700+ regulations)
- **Constitutional Court** — cons.judicial.gov.tw (868 interpretations + constitutional judgments, including full reasoning and offline cache)
Built with Python and [FastMCP](https://github.com/modelcontextprotocol/python-sdk). Pure tool wrapper only hits the above three official Taiwan sources, without sending any other network requests.
## Features
| Function | Description |
|------|------|
| **8 MCP Tools** | Judgment search/full-text, regulation query, interpretation/judgment query, citation graph |
| **Offline Cache** | 868 interpretations and constitutional judgments (including full reasoning and opinions) returned immediately from local JSON |
| **Citation Graph** | Extract all cited interpretations/judgments from reasoning, tracing constitutional doctrine evolution |
| **Full-text Search** | Judgment keyword search + interpretation/judgment full-text search |
| **Hybrid Request Strategy** | Default use httpx direct hit (~0.25s), automatically switch to Playwright to brush cookie and continue when Judicial Yuan F5 WAF is triggered |
## ⚡ Installation (PyPI, recommended)
```bash
pip install mcp-taiwan-legal-db
```
> **Debian / Ubuntu / WSL Note**: System Python is protected by PEP 668, direct `pip install` will be blocked. Please use:
> - `pipx install mcp-taiwan-legal-db` (recommended, automatically create isolated venv, standard CLI tool installation)
> - or `pip install --user --break-system-packages mcp-taiwan-legal-db`
After installation, entry point `mcp-taiwan-legal-db` will be in PATH. **Connect to Claude Code** (any project can use):
```bash
claude mcp add taiwan-legal-db mcp-taiwan-legal-db --scope user
```
Then, restart `/mcp` connection, Claude will automatically use 8 MCP tools during natural language queries.
**Optional — F5 WAF fallback**:
```bash
playwright install chromium # Only used when Judicial Yuan WAF is triggered, idle otherwise
```
## Development Environment Setup
Below is the process to clone, modify, and run tests:
```bash
# 1. Clone repo
git clone https://github.com/lawchat-oss/mcp-taiwan-legal-db.git
cd mcp-taiwan-legal-db
# 2. Create and initialize virtual environment
python3 -m venv .venv
.venv/bin/pip install --upgrade pip
.venv/bin/pip install -e .
# 3. Install Playwright Chromium (only used when Judicial Yuan WAF is triggered, generally not started)
.venv/bin/playwright install chromium
# 4. Verify server can start and register 8 tools
.venv/bin/python -c "
import asyncio
from mcp_server.server import mcp
print('Server:', mcp.name)
tools = asyncio.run(mcp.list_tools())
print('Tools:', [t.name for t in tools])
assert len(tools) == 8, f'Expected 8 tools, got {len(tools)}'
print('✓ Setup OK')
"
```
**Expected output:**
```
Server: Taiwan Legal Database
Tools: ['search_judgments', 'get_judgment', 'query_regulation', 'get_pcode', 'search_regulations', 'get_interpretation', 'search_interpretations', 'get_citations']
✓ Setup OK
```
The above setup is complete if no errors occur. The repo root directory already contains a `.mcp.json` file, which will automatically load this server for any Claude Code session opened in this directory.
## Tools Available
8 MCP tools, all read-only, all hitting Taiwan government's public databases.
### Regulations and Judgments
| Tool | Purpose | Typical Call |
|---|---|---|
| `search_judgments` | Search Judicial Yuan judgment database | `search_judgments(keyword="pre-sale house delay delivery", case_type="civil")` |
| `get_judgment` | Get single judgment full-text by JID or URL | `get_judgment(jid="TPSM,114,台上,3753,20251112,1")` |
| `query_regulation` | Query regulation articles/ranges/full-text/amendment history | `query_regulation(law_name="Civil Code", article_no="184")` |
| `get_pcode` | Parse regulation name to pcode (regulation code) | `get_pcode(law_name="Lawyers Act")` |
| `search_regulations` | Search 11,700+ regulations with keywords | `search_regulations(keyword="labor")` |
### Constitutional Court
| Tool | Purpose | Typical Call |
|---|---|---|
| `get_interpretation` | Interpretation/judgment full-text (offline cache) | `get_interpretation("釋字748", reasoning_keyword="marriage")` |
| `search_interpretations` | Search interpretations/judgments (issues + full-text) | `search_interpretations(keyword="freedom of assembly")` |
| `get_citations` | Citation graph (trace back) | `get_citations("釋字748", include_context=True)` |
### Tool Details
<details>
<summary><b><code>search_judgments</code></b></summary>
Search Judicial Yuan judgment system. Supports:
- **Precise case number query** (fast, HTTP GET): set `case_word` + `case_number` + `year_from`
- **Full-text keyword search**: set `keyword`
- **Judgment main text filtering**: `main_text="defendant should transfer"` + `keyword="nomination registration"` → find defendant losing cases of nomination registration
- Filter by `court`, `case_type` (civil/criminal/administrative/disciplinary), `year_from`/`year_to`
- Results automatically sorted by court level (Supreme → High → Local)
**Important**: To query a specific case number, **must** use `case_word`+`case_number`, not put in `keyword`.
```python
# ✅ Correct — Query 114 台上 3753 Supreme Court
search_judgments(case_word="台上", case_number="3753", year_from=114, court="Supreme Court")
# ✅ Correct — Full-text search
search_judgments(keyword="pre-sale house delay delivery")
# ❌ Wrong — Put case number in keyword
search_judgments(keyword="114年度台上字第3753號")
```
</details>
<details>
<summary><b><code>get_judgment</code></b></summary>
Get single judgment structured full-text.
- Input: `jid` (from `search_judgments` result) or `url`
- Output: `{case_id, court, date, main_text, facts, reasoning, cited_statutes, cited_cases, full_text, source_url}`
- HTTP GET data.aspx get full-text
- Result cache 30 days
```python
get_judgment(jid="TPSM,114,台上,3753,20251112,1")
```
Single judgment may exceed 10,000 tokens. Recommend using `search_judgments` to get metadata, only fetch full-text when user explicitly needs it.
</details>
<details>
<summary><b><code>query_regulation</code></b></summary>
Query National Regulation Database.
```python
# Single article
query_regulation(law_name="Civil Code", article_no="184")
# Article range
query_regulation(law_name="Civil Code", from_no="184", to_no="198")
# Complete regulation
query_regulation(law_name="Lawyers Act")
# With amendment history
query_regulation(law_name="Labor Standards Act", article_no="23", include_history=True)
```
Supports `law_name` (automatically parse pcode via `get_pcode`) or directly pass `pcode`. Sub-articles like `247-1`, `15-1` are supported.
</details>
<details>
<summary><b><code>get_interpretation</code></b></summary>
Get interpretation/judgment full-text. Default layer returns immediately from local JSON cache.
**Layered design** (save context):
| Layer | Trigger condition | Offline? |
|------|---------|-------|
| Default layer (serial number/date/issue/interpretation text) | Always return | ✓ |
| Reasoning fragment | `reasoning_keyword="keyword"` | ✓ |
| Full reasoning (up to 15,000 characters) | `include_reasoning=True` | ✓ |
| Opinion fragment | `opinions_keyword="keyword"` | ✓ |
| Full opinion | `include_opinions=True` | ✓ |
```python
# Default layer (offline, ~0ms)
get_interpretation("釋字748")
# Search keyword in reasoning
get_interpretation("釋字748", reasoning_keyword="marriage freedom")
# Locate specific justice in opinion
get_interpretation("釋字499", opinions_keyword="Lin Zi-Yi")
# New constitutional judgment
get_interpretation("111年憲判字第1號")
```
Recommend using keyword fragment mode to locate, only open full-text mode when needed.
</details>
<details>
<summary><b><code>search_interpretations</code></b></summary>
Search interpretations/judgments. Keywords match title, issues, and full-text.
```python
# Full-text search (search issues + full-text)
search_interpretations(keyword="freedom of assembly")
# Filter by year (new system)
search_interpretations(keyword="freedom of speech", year=112)
# List last 10 interpretations
search_interpretations(number_from=804, number_to=813)
```
</details>
<details>
<summary><b><code>get_citations</code></b></summary>
Extract cited interpretations/judgments from reasoning. Tracing direction: query specified judgment **cited which prior judgments**.
```python
get_citations("釋字748")
# → citations: [釋字第242號, 釋字第362號, 釋字第365號, ...]
# With 80-character context
get_citations("釋字748", include_context=True)
```
</details>
## Example Queries
```
「Query Civil Code Article 184」
「Search for Supreme Court judgments related to pre-sale house delay delivery」
「What are the key points of 釋字 748 reasoning?」
「Which interpretations discuss freedom of assembly?」
「Which prior interpretations did 釋字 748 cite?」
「Query 111年憲判字第 1 號」
```
## Register to Your Claude Client
Follow the corresponding section for your Claude client.
### Claude Code (CLI)
Claude Code will automatically load the `.mcp.json` file in the project root directory. This repo already contains one:
```json
{
"mcpServers": {
"taiwan-legal-db": {
"command": ".venv/bin/python",
"args": ["-m", "mcp_server.server"]
}
}
}
```
**Zero setup**: `cd` into the repo and run `claude`. MCP server list will see `taiwan-legal-db`, and this directory will not have other servers.
**Share with teammates**: `.mcp.json` is already committed to the repo. Anyone cloning it and running Quick Start will automatically complete MCP registration.
**Add to other projects** (you want to use this MCP in another directory): use `claude mcp add` to add with project scope:
```bash
cd /path/to/your/other/project
claude mcp add taiwan-legal-db --scope project -- \
/absolute/path/to/mcp-taiwan-legal-db/.venv/bin/python \
-m mcp_server.server
```
This will write a `.mcp.json` file in your another project's root directory. To use it in every project, change `--scope project` to `--scope user`.
### Claude Desktop (macOS / Windows)
Claude Desktop uses a global settings file:
- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
- **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
- **Windows (Microsoft Store / WinGet / MSIX installation)**: `C:\Users\<YourName>\AppData\Local\Packages\Claude_pzs8sxrjxfjjc\LocalCache\Roaming\Claude\claude_desktop_config.json`
**Fastest way to open**: In Claude Desktop, click menu (not window) → **Settings** → **Developer** → **Edit Config**. If the file does not exist, Claude Desktop will automatically create it.
Add the following content under `mcpServers` (merge with existing content):
```json
{
"mcpServers": {
"taiwan-legal-db": {
"command": "/absolute/path/to/mcp-taiwan-legal-db/.venv/bin/python",
"args": ["-m", "mcp_server.server"],
"cwd": "/absolute/path/to/mcp-taiwan-legal-db"
}
}
}
```
Replace `/absolute/path/to/mcp-taiwan-legal-db` with your actual clone path. `cwd` field is required for Python to find `mcp_server` package.
**Save and restart Claude Desktop** (not just close window — macOS use ⌘Q, Windows right-click toolbar icon → Quit). Settings file will only reload after restart.
### Claude Cowork (Pro and above plans)
Claude Cowork runs in Claude Desktop, **sharing the same `claude_desktop_config.json`** — no additional Cowork settings file. Any MCP server you register in Claude Desktop will automatically bridge into Cowork's sandbox VM via Claude Desktop SDK.
**Setup steps**:
1. Add `taiwan-legal-db` to `claude_desktop_config.json` as above
2. **Completely close and restart Claude Desktop** — also restart Cowork
3. Open a Cowork session, `taiwan-legal-db` tools are available
**Note**: Cowork currently available in Claude Pro / Max / Team / Enterprise plans, and can only access folders you explicitly authorize. MCP server itself runs on your host (not in Cowork VM), communicating through Desktop SDK bridge.
### Other MCP-compatible clients
Any client following [Model Context Protocol specification](https://modelcontextprotocol.io/) can use this server. The command to start is always:
```
.venv/bin/python -m mcp_server.server
```
...with `cwd` setting to repo root directory (Python can find `mcp_server` package). Refer to your client documentation to find where `mcpServers` JSON block is located.
Troubleshooting
**`ModuleNotFoundError: No module named 'mcp_server'`**
→ You didn't run `pip install -e .` in the venv. Go back to Quick Start step 2.
**`FileNotFoundError: data/pcode_all.json`**
→ The built-in `mcp_server/data/pcode_all.json` file is missing or deleted. Restore it with `git checkout mcp_server/data/pcode_all.json` or trigger a re-download:
```bash
.venv/bin/python -m mcp_server.updater
```
**MCP client reports "Server startup failed"**
→ Run the verification command in Quick Start step 3 directly. If it fails, it means the import chain is broken - check the traceback. If it passes, the issue is with the MCP client's startup settings (wrong path or cwd).
**`ssl.SSLCertVerificationError: ... Missing Subject Key Identifier`**
→ This is a widespread rejection of TWCA Global Root CA by OpenSSL 3.6+, **not a certifi old issue**. This repo uses the [`truststore`](https://github.com/sethmlarson/truststore) package to let Python use the native trust store of the operating system (macOS Security framework, Windows CryptoAPI, Linux system CA), **all paths retain complete SSL verification (`verify=True`)**, without using `verify=False`. This works normally on macOS, Windows, and Linux with OpenSSL <3.6. Linux environments with OpenSSL 3.6+ (Fedora 40+, future Ubuntu LTS) may still have issues, and welcome issues reporting.
---
## WAF Handling Mechanism
The Judicial Yuan `judgment.judicial.gov.tw` has deployed F5 BIG-IP ASM WAF, and pure HTTP requests may be blocked (returning a fixed 245 bytes "Request Rejected").
This project adopts a hybrid strategy:
- Default uses httpx to request directly (~0.25s)
- Detects blocking (response contains `Request Rejected` or JS challenge marker `bobcmn` / `TSPD`) and automatically falls back to Playwright to run the JS challenge once
- Obtains TSPD cookies and persists them to `mcp_server/data/.judicial_cookies.json` (0600 permission, gitignored)
- Subsequent queries continue to use httpx with cookies
`cons.judicial.gov.tw` (constitutional interpretations) and `law.moj.gov.tw` (regulations) do not have this issue and do not go through the WAF process.
---
## Data Sources and Statistics
All data comes from Taiwan government's **public** databases. No external network calls are made:
| Source | Domain | Description |
|------|------|------|
| Judicial Yuan Judgment System | judgment.judicial.gov.tw | Judgment search and full text |
| Constitutional Court of Judicial Yuan | cons.judicial.gov.tw | Constitutional interpretations and constitutional judgments |
| National Database of Laws and Regulations | law.moj.gov.tw | Regulatory texts and amendments |
`mcp_server/config.py:ALLOWED_DOMAINS` enforces an allow-list to prevent scraping URLs outside these domains.
### Constitutional Court Data Statistics
| Dataset | Number of entries | With reasons | With opinions | File size |
|--------|------|---------|---------|---------|
| Old interpretations (old_cases.json) | 813 | 734 | 370 | 7.4 MB |
| New constitutional judgments (new_cases.json) | 55 | 55 | 55 | 1.8 MB |
## Cache
| Data type | TTL | Location |
|---|---|---|
| Judgment full text | 30 days | `mcp_server/data/cache/legal_mcp.db` (SQLite, created at first startup) |
| Search results | 24 hours | Same as above |
| Regulatory texts | 7 days | Same as above |
| pcode metadata | 30 days | Same as above |
| Constitutional interpretations/judgments | Local JSON (no expiration) | `mcp_server/data/old_cases.json`, `new_cases.json` |
Clear all: delete `mcp_server/data/cache/legal_mcp.db`. Cache files are in `.gitignore`.
## Automatic Update of pcode_all.json
The server checks the timestamp of `mcp_server/data/pcode_all.json` at startup. If the last update was before the recent Saturday, it triggers a re-fetch from the official API of `law.moj.gov.tw` in the background. Failure is recorded as a warning and does not block startup.
Manual update:
```bash
.venv/bin/python -m mcp_server.updater
```
---
## Project Structure
```
mcp-taiwan-legal-db/
├── .gitignore
├── .mcp.json # For automatic registration of Claude Code sessions in the folder
├── LICENSE # MIT (code)
├── DATA_LICENSE # CC0 1.0 (constitutional court data)
├── SOURCES.md # Data source description
├── CITATION.cff # Academic citation format
├── README.md # This file (Traditional Chinese)
├── README.en.md # English version
├── pyproject.toml # Package metadata and dependencies
└── mcp_server/
├── __init__.py
├── server.py # FastMCP entry - defines 8 @mcp.tool() functions
├── config.py # URL, court codes, cache TTL, allowed domains
├── updater.py # Independent pcode_all.json update script
├── cache/db.py # SQLite cache layer
├── data/
│ ├── pcode_all.json # 11,700+ regulations (built-in, ~780 KB)
│ ├── law_histories.json # Amendment history (built-in, ~9.6 MB)
│ ├── old_cases.json # 813 old interpretations (built-in, ~7.4 MB)
│ └── new_cases.json # 55 new constitutional judgments (built-in, ~1.8 MB)
├── models/ # Judgment / Regulation dataclass
├── parsers/ # HTML parsers for judgments and regulations
├── tools/
│ ├── judicial_search.py # search_judgments
│ ├── judicial_doc.py # get_judgment
│ ├── regulations.py # query_regulation, get_pcode, search_regulations
│ └── constitutional_court.py # get_interpretation, search_interpretations, get_citations
└── tests/ # pytest tests
```
## Running Tests
```bash
.venv/bin/pip install -e ".[dev]"
.venv/bin/pytest mcp_server/tests/ -v
```
---
## About
Maintained by [LawChat](https://lawchat.com.tw) - A Taiwan legal AI platform.
- Official website: [lawchat.com.tw](https://lawchat.com.tw)
- Contact: opensource@lawchat.com.tw
- Report issues: [GitHub Issues](https://github.com/lawchat-oss/mcp-taiwan-legal-db/issues)
Best-effort maintenance - we will try to keep up with upstream (Judicial Yuan, Ministry of Justice) page changes, but do not guarantee issue response time.
## License
**Code**: [MIT License](LICENSE)
**Constitutional Court Data**: [CC0 1.0](DATA_LICENSE) (public domain contribution) - anyone can freely use, modify, and distribute without obtaining permission or attribution. Academic citation format please refer to [CITATION.cff](CITATION.cff).
Judgment and regulation data sources: [Judicial Yuan](https://judgment.judicial.gov.tw), [Ministry of Justice](https://law.moj.gov.tw) (government open data).
Constitutional Court data source: [Constitutional Court of Judicial Yuan](https://cons.judicial.gov.tw) (public domain under Article 9 of the Copyright Act of the Republic of China). See [SOURCES.md](SOURCES.md) for details.
## Disclaimer
This is an **unofficial** tool for querying publicly-available Taiwan legal databases. It is not affiliated with, endorsed by, or authorized by the Judicial Yuan, the Ministry of Justice, or any Taiwan government agency.
The data returned by this tool reflects the state of the upstream official sources at the time of query. It may be cached (see TTLs above), and **must not be treated as legal advice or a substitute for the authoritative official sources**. Always verify against for any legal or official purpose.
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
markitdown
Python tool for converting files and office documents to Markdown.
markitdown
MarkItDown-MCP is a lightweight server for converting URIs to Markdown.
Filesystem
Node.js MCP Server for filesystem operations with dynamic access control.
TrendRadar
TrendRadar: Your hotspot assistant for real news in just 30 seconds.
mempalace
The highest-scoring AI memory system ever benchmarked. And it's free.
mempalace
The highest-scoring AI memory system ever benchmarked. And it's free.