Skip to main content
Glama
README.md
[![MseeP.ai Security Assessment Badge](https://mseep.net/pr/rabqatab-lexlink-ko-mcp-badge.png)](https://mseep.ai/app/rabqatab-lexlink-ko-mcp)

<div align="center">
  <img src="assets/LexLink_logo.png" alt="LexLink Logo" width="200"/>
  <h1>LexLink - Korean National Law Information MCP Server</h1>
</div>

**🌐 Read this in other languages:** **English** | [ν•œκ΅­μ–΄ (Korean)](README_kr.md)

[![Kakao PlayMCP 10 3rd Prize](https://img.shields.io/badge/Kakao_PlayMCP_10-3rd_Prize_πŸ†-CD7F32)](https://playmcp.kakao.com/)
[![smithery badge](https://smithery.ai/badge/@rabqatab/lexlink-ko-mcp)](https://smithery.ai/server/@rabqatab/lexlink-ko-mcp)
[![MCP](https://img.shields.io/badge/MCP-Compatible-blue)](https://modelcontextprotocol.io)
[![Python](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)

LexLink is an MCP (Model Context Protocol) server that exposes the Korean National Law Information API ([open.law.go.kr](http://open.law.go.kr)) to AI agents and LLM applications. It enables AI systems to search, retrieve, and analyze Korean legal information through standardized MCP tools.

## Features

- **54 MCP Tools + 2 MCP Resources** for comprehensive Korean law information access
  - Search and retrieve Korean laws (effective date & announcement date)
  - Search and retrieve English-translated laws
  - Search and retrieve administrative rules (ν–‰μ •κ·œμΉ™)
  - Query specific articles, paragraphs, and sub-items
  - Law-ordinance linkage (법령-μžμΉ˜λ²•κ·œ 연계)
  - Delegated law information (μœ„μž„λ²•λ Ή)
  - **Phase 3 - Case Law & Legal Research**
    - Court precedents (νŒλ‘€)
    - Constitutional Court decisions (ν—Œμž¬κ²°μ •λ‘€)
    - Legal interpretations (법령해석둀)
    - Administrative appeal decisions (ν–‰μ •μ‹¬νŒλ‘€)
  - **Phase 4 - Article Citation Extraction**
    - Extract legal citations from any law article (100% accuracy)
  - **NEW: Phase 5 - AI-Powered Search**
    - Semantic search for natural language queries (aiSearch)
    - Related laws discovery (aiRltLs_search)
  - **MCP Resources - Law ID Cache**
    - Cached mapping of ~20 frequently-used law names to stable 법령ID codes
    - Template lookup by Korean name or abbreviation (`lexlink://law/{name}`)
    - Dynamic caching: search results automatically populate the cache
- **Smart Features** (inspired by [korean-law-mcp](https://github.com/chrisryugj/korean-law-mcp)):
  - **Intelligent Caching** - Per-tool TTL caching (search 1hr, articles 24hr, AI search 30min)
  - **Law Name Resolution** - Auto-resolves Korean abbreviations (μžν†΅λ²•β†’μžλ³Έμ‹œμž₯κ³Ό κΈˆμœ΅νˆ¬μžμ—…μ— κ΄€ν•œ 법λ₯ ), 52 seed aliases + dynamic learning
  - **Chain Tools** - Multi-step research workflows in one call (Phase 9)
- **100% Semantic Validation** - All Phase 1-5 tools confirmed returning real law data
- **Error Handling** - Actionable error messages with resolution hints
- **Korean Text Support** - Proper UTF-8 encoding for Korean characters
- **Response Formats** - JSON (default), HTML, or XML (multiple formats supported)

## Project Status

πŸŽ‰ **Production Ready - Phase 9 Complete!**

| Metric | Status |
|--------|--------|
| **Tools Implemented** | 54/54 (100%) βœ… |
| **Semantic Validation** | 26/26 (Phase 1-5 tools) βœ… |
| **MCP Prompts** | 9/9 (100%) βœ… |
| **MCP Resources** | 2 (1 static + 1 template) βœ… |
| **API Coverage** | ~28% of 191+ endpoints |
| **LLM Integration** | βœ… Validated (Gemini) |
| **Code Quality** | Clean, documented, tested |
| **Version** | v2.1.0 |

**Latest:** v2.1.0 β€” 54 tools (Phase 9 added), intelligent caching (`cache.py`), law name resolution (`resolver.py`), chain tools for multi-step research workflows.

## Prerequisites

- **Python 3.10+**
- **law.go.kr OC identifier**: Register at [open.law.go.kr](https://open.law.go.kr)

## Quick Start

### 1. Install Dependencies

```bash
uv sync
```

### 2. Configure Your OC Identifier

**Option A: Environment Variable (Recommended)**
```bash
# Set OC in your environment
export OC=your_id_here
```

**Option B: Pass in Tool Arguments**
```python
# Override OC in each tool call
eflaw_search(query="법령λͺ…", oc="your_id")
```

### 3. Run the Server

```bash
# Stdio transport (for Claude Code, Cursor, etc.)
OC=your_oc uv run stdio

# HTTP transport (for Kakao PlayMCP)
OC=your_oc TRANSPORT=http uv run serve
```

## Available Tools

### Phase 1: Core Law APIs (6 tools)

#### 1. `eflaw_search` - Search Laws by Effective Date
Search for laws organized by effective date (μ‹œν–‰μΌ κΈ°μ€€).

```python
eflaw_search(
    query="μžλ™μ°¨κ΄€λ¦¬λ²•",      # Search keyword
    display=10,                # Results per page
    type="XML",                # Response format
    ef_yd="20240101~20241231"  # Optional date range
)
```

#### 2. `law_search` - Search Laws by Announcement Date
Search for laws organized by announcement date (곡포일 κΈ°μ€€).

```python
law_search(
    query="민법",
    display=10,
    type="XML"
)
```

#### 3. `eflaw_service` - Retrieve Law Content (Effective Date)
Get full law text and articles by effective date.

> **IMPORTANT:** For specific article queries (e.g., "제174쑰"), use the `jo` parameter. Some laws have 400+ articles and responses can exceed 1MB without `jo`.

```python
# Get specific article (RECOMMENDED)
eflaw_service(
    mst="279823",              # Law MST
    jo="017400",               # Article 174 (제174쑰)
    type="XML"
)

# Get full law (WARNING: large response)
eflaw_service(
    id="001823",
    type="XML"
)
```

#### 4. `law_service` - Retrieve Law Content (Announcement Date)
Get full law text and articles by announcement date.

> **IMPORTANT:** For specific article queries (e.g., "제174쑰"), use the `jo` parameter. Some laws have 400+ articles and responses can exceed 1MB without `jo`.

```python
# Get specific article (RECOMMENDED)
law_service(
    mst="279823",              # Law MST
    jo="017400",               # Article 174 (제174쑰)
    type="XML"
)
```

#### 5. `eflaw_josub` - Query Article/Paragraph (Effective Date)
**Best tool for querying specific articles.** Returns only the requested article/paragraph.

```python
eflaw_josub(
    mst="279823",              # Law MST
    jo="017400",               # Article 174 (제174쑰)
    type="XML"
)
# jo format: "XXXXXX" where first 4 digits = article (zero-padded), last 2 = branch (00=main)
# Examples: "017400" (제174쑰), "000300" (제3쑰), "001502" (제15쑰의2)
```

#### 6. `law_josub` - Query Article/Paragraph (Announcement Date)
**Best tool for querying specific articles.** Returns only the requested article/paragraph.

```python
law_josub(
    mst="279823",              # Law MST
    jo="017200",               # Article 172 (제172쑰)
    type="XML"
)
```

### Phase 2: Extended APIs (9 tools)

#### 7. `elaw_search` - Search English-Translated Laws
Search for Korean laws translated to English.

```python
elaw_search(
    query="employment",
    display=10,
    type="XML"
)
```

#### 8. `elaw_service` - Retrieve English Law Content
Get full English-translated law text.

```python
elaw_service(
    id="009589",
    type="XML"
)
```

#### 9. `admrul_search` - Search Administrative Rules
Search administrative rules (ν›ˆλ Ή, 예규, κ³ μ‹œ, 곡고, μ§€μΉ¨).

```python
admrul_search(
    query="학ꡐ",
    display=10,
    type="XML"
)
```

#### 10. `admrul_service` - Retrieve Administrative Rule Content
Get full administrative rule text with annexes.

```python
admrul_service(
    id="62505",
    type="XML"
)
```

#### 11. `lnkLs_search` - Search Law-Ordinance Linkage
Find laws linked to local ordinances.

```python
lnkLs_search(
    query="건좕",
    display=10,
    type="XML"
)
```

#### 12. `lnkLsOrdJo_search` - Search Ordinance Articles by Law
Find ordinance articles linked to specific law articles.

```python
lnkLsOrdJo_search(
    knd="002118",              # Law ID
    display=10,
    type="XML"
)
```

#### 13. `lnkDep_search` - Search Law-Ordinance Links by Ministry
Find laws linked to ordinances by government ministry.

```python
lnkDep_search(
    org="1400000",             # Ministry code
    display=10,
    type="XML"
)
```

#### 14. `drlaw_search` - Retrieve Law-Ordinance Linkage Statistics
Get linkage statistics table (HTML format).

```python
drlaw_search(
    lid="001823",              # Law ID
    type="HTML"
)
```

#### 15. `lsDelegated_service` - Retrieve Delegated Law Information
Get information about delegated laws, rules, and ordinances.

```python
lsDelegated_service(
    id="001823",
    type="XML"
)
```

### Phase 3: Case Law & Legal Research (8 tools - NEW!)

> **Tip:** All `*_service` tools in Phase 3 support a `sections="summary"` parameter to return only a brief summary instead of the full document text.

#### 16. `prec_search` - Search Court Precedents
Search Korean court precedents from Supreme Court and lower courts.

```python
prec_search(
    query="λ‹΄λ³΄κΆŒ",
    display=10,
    type="XML",
    curt="λŒ€λ²•μ›"             # Optional: Court name filter
)
```

#### 17. `prec_service` - Retrieve Court Precedent Full Text
Get complete court precedent text with case details.

```python
prec_service(
    id="228541",
    type="XML"
)
```

#### 18. `detc_search` - Search Constitutional Court Decisions
Search Korean Constitutional Court decisions.

```python
detc_search(
    query="벌금",
    display=10,
    type="XML"
)
```

#### 19. `detc_service` - Retrieve Constitutional Court Decision Full Text
Get complete Constitutional Court decision text.

```python
detc_service(
    id="58386",
    type="XML"
)
```

#### 20. `expc_search` - Search Legal Interpretations
Search legal interpretation precedents issued by government agencies.

```python
expc_search(
    query="μž„μ°¨",
    display=10,
    type="XML"
)
```

#### 21. `expc_service` - Retrieve Legal Interpretation Full Text
Get complete legal interpretation text.

```python
expc_service(
    id="334617",
    type="XML"
)
```

#### 22. `decc_search` - Search Administrative Appeal Decisions
Search Korean administrative appeal decisions.

```python
decc_search(
    query="*",                # Search all decisions
    display=10,
    type="XML"
)
```

#### 23. `decc_service` - Retrieve Administrative Appeal Decision Full Text
Get complete administrative appeal decision text.

```python
decc_service(
    id="243263",
    type="XML"
)
```

### Phase 4: Article Citation Extraction (1 tool - NEW!)

#### 24. `article_citation` - Extract Citations from Law Article
Extract all legal citations referenced by a specific law article.

```python
# First, search for the law to get MST
eflaw_search(query="건좕법")  # Returns MST: 268611

# Then extract citations
article_citation(
    mst="268611",              # Law MST from search result
    law_name="건좕법",          # Law name
    article=3                  # Article number (제3쑰)
)
```

**Response:**
```json
{
    "success": true,
    "law_name": "건좕법",
    "article": "제3쑰",
    "citation_count": 12,
    "internal_count": 4,
    "external_count": 8,
    "citations": [
        {
            "type": "external",
            "target_law_name": "γ€Œκ΅­ν† μ˜ κ³„νš 및 μ΄μš©μ— κ΄€ν•œ 법λ₯ γ€",
            "target_article": 56,
            "target_paragraph": 1
        }
    ]
}
```

**Key Features:**
- 100% accuracy via HTML parsing (not LLM-based)
- Zero API cost (no external LLM calls)
- ~350ms average extraction time
- Distinguishes internal vs external citations

### Phase 5: AI-Powered Search (2 tools - NEW!)

#### 25. `aiSearch` - AI-Powered Semantic Law Search
⭐ **PREFERRED TOOL for vague or natural language queries.** Use this FIRST when user's intent is unclear or conversational.

Uses intelligent/semantic search to find relevant law articles with full article text.

```python
aiSearch(
    query="λΊ‘μ†Œλ‹ˆ 처벌",           # Natural language query
    search=0,                      # 0: law articles, 1: appendix, 2: admin rules, 3: admin appendix
    display=20,                    # Results per page
    page=1,                        # Page number
    type="JSON"                    # Response format (JSON default)
)
```

**Best for:** Natural language queries like "μŒμ£Όμš΄μ „ 벌금", "이혼 μž¬μ‚°λΆ„ν• ", "상속 문제"

#### 26. `aiRltLs_search` - AI-Powered Related Laws Search
⭐ **PREFERRED TOOL for discovering related laws from vague topics.** Use this when user wants to explore laws around a general subject.

Finds laws semantically related to a given law name or keyword.

```python
aiRltLs_search(
    query="민법",                  # Law name or keyword
    search=0,                      # 0: law articles, 1: admin rule articles
    type="JSON"                    # Response format (JSON default)
)
```

**Best for:** Finding related laws like "민법" β†’ 상법, μ˜λ£Œλ²•, μ†Œμ†‘μ΄‰μ§„λ²•

### Phase 7: Extended Legal Information (18 tools)

| Category | Tools |
|----------|-------|
| μžμΉ˜λ²•κ·œ (Local Ordinances) | `ordin_search`, `ordin_service`, `ordinLsCon_search` |
| μ‘°μ•½ (Treaties) | `trty_search`, `trty_service` |
| 법령정보 μ§€μ‹λ² μ΄μŠ€ (Knowledge Base) | `lstrm_ai_search`, `dlytrm_search`, `lstrm_rlt_search`, `dlytrm_rlt_search`, `lstrm_rlt_jo_search`, `jo_rlt_lstrm_search`, `ls_rlt_search` |
| μœ„μ›νšŒ κ²°μ •λ¬Έ (Committee Decisions) | `committee_search`, `committee_service` |
| μ€‘μ•™λΆ€μ²˜ 1μ°¨ 해석 (Ministry Interpretations) | `cgm_expc_search`, `cgm_expc_service` |
| νŠΉλ³„ν–‰μ •μ‹¬νŒ (Special Appeals) | `special_decc_search`, `special_decc_service` |

### Phase 9: Chain Tools (5 tools)

Inspired by [korean-law-mcp](https://github.com/chrisryugj/korean-law-mcp), these tools run multi-step research workflows in a single call β€” eliminating the need for an LLM to orchestrate sequential tool calls manually.

| Tool | Description |
|------|-------------|
| `chain_full_research` | Complete legal research: statutes + precedent analysis + interpretations |
| `chain_amendment_track` | Revision history + article-level diff across amendments |
| `chain_dispute_prep` | All case law sources across 4 databases (νŒλ‘€, ν—Œμž¬κ²°μ •λ‘€, 법령해석둀, ν–‰μ •μ‹¬νŒλ‘€) |
| `chain_law_system` | Full law hierarchy: delegation tree + admin rules + ordinances |
| `cache_stats` | Cache and resolver performance monitoring |

### Tool Selection Guide

When searching Korean law, select tools based on query clarity:

| Query Type | Recommended Tools | Examples |
|------------|-------------------|----------|
| πŸ” **Vague/Natural language** | `aiSearch`, `aiRltLs_search` | "μŒμ£Όμš΄μ „ 처벌", "이혼 μž¬μ‚°λΆ„ν• " |
| πŸ“‹ **Specific law/article** | `eflaw_search`, `law_search` | "ν˜•λ²• 제148쑰의2", "민법 μƒμ†νŽΈ" |
| βš–οΈ **Case law** | `prec_search`, `detc_search` | "λŒ€λ²•μ› 2023λ‹€12345" |
| πŸ”— **Related laws** | `aiRltLs_search` | "민법과 κ΄€λ ¨λœ 법λ₯ " |

## Configuration

### Environment Variables

| Variable | Default | Description |
|----------|---------|-------------|
| `OC` | *(required)* | law.go.kr API identifier (email local part) |
| `LEXLINK_BASE_URL` | `http://www.law.go.kr` | API base URL |
| `LEXLINK_TIMEOUT` | `60` | HTTP request timeout in seconds |
| `SLIM_RESPONSE` | *(unset)* | Set `true` to remove redundant raw XML when parsed data exists (for PlayMCP) |
| `TRANSPORT` | `sse` | Transport type: `sse` or `http` |

### OC Priority

When resolving the OC identifier:
1. **Tool argument** (highest priority) - `oc` parameter in tool call
2. **Environment variable** - `OC` env var (set via .env or HTTP header middleware)

## Usage Examples

### Example 1: Basic Search

```python
# Search for automobile management law
result = eflaw_search(
    query="μžλ™μ°¨κ΄€λ¦¬λ²•",
    display=5,
    type="XML"
)

# Returns:
{
    "status": "ok",
    "request_id": "uuid",
    "upstream_type": "XML",
    "data": {
        # Law search results...
    }
}
```

### Example 2: Search with Date Range

```python
# Find laws effective in 2024
result = eflaw_search(
    query="ꡐ톡",
    ef_yd="20240101~20241231",
    type="XML"
)
```

### Example 3: Error Handling

```python
# Missing OC parameter
result = eflaw_search(query="test")

# Returns helpful error:
{
    "status": "error",
    "error_code": "MISSING_OC",
    "message": "OC parameter is required but not provided.",
    "hints": [
        "1. Tool argument: oc='your_value'",
        "2. Environment variable: OC=your_value"
    ]
}
```

## Golden MCP Tool Trajectories

These examples demonstrate real-world conversation flows showing how LLMs interact with LexLink tools to answer legal research questions.

### Trajectory 1: Basic Law Research
**User Query:** "What is Article 20 of the Civil Code?"

**Tool Calls:**
1. `law_search(query="민법", display=50, type="XML")` β†’ Find Civil Code ID
2. `law_service(id="000021", jo="002000", type="XML")` β†’ Retrieve Article 20 text

**Result:** LLM provides formatted explanation of Civil Code Article 20 with full legal text and context.

---

### Trajectory 2: Court Precedent Analysis
**User Query:** "Find recent Supreme Court precedents about security interests"

**Tool Calls:**
1. `prec_search(query="λ‹΄λ³΄κΆŒ", curt="λŒ€λ²•μ›", display=50, type="XML")` β†’ Search Supreme Court precedents
2. `prec_service(id="228541", type="XML")` β†’ Retrieve top precedent details

**Result:** LLM summarizes key precedents with case numbers, dates, and holdings related to security interests.

---

### Trajectory 3: Cross-Phase Legal Research
**User Query:** "How does the Labor Standards Act handle overtime, and are there relevant court precedents?"

**Tool Calls:**
1. `eflaw_search(query="κ·Όλ‘œκΈ°μ€€λ²•", display=50, type="XML")` β†’ Find Labor Standards Act
2. `eflaw_service(id="001234", jo="005000", type="XML")` β†’ Retrieve Article 50 (overtime provisions)
3. `prec_search(query="κ·Όλ‘œκΈ°μ€€λ²• μ—°μž₯근둜", display=30, type="XML")` β†’ Search overtime precedents
4. `prec_service(id="234567", type="XML")` β†’ Retrieve leading precedent

**Result:** LLM provides comprehensive analysis combining statutory text with judicial interpretation, showing how courts apply the overtime provisions.

---

### Trajectory 4: Constitutional Review
**User Query:** "Has the Constitutional Court reviewed laws about fines?"

**Tool Calls:**
1. `detc_search(query="벌금", display=50, type="XML")` β†’ Search Constitutional Court decisions
2. `detc_service(id="58386", type="XML")` β†’ Retrieve decision full text
3. `law_search(query=<law_name_from_decision>, type="XML")` β†’ Find related law for context

**Result:** LLM explains Constitutional Court holdings on fine-related provisions and their impact on specific laws.

---

### Trajectory 5: Administrative Law Research
**User Query:** "What administrative rules exist for schools, and are there related legal interpretations?"

**Tool Calls:**
1. `admrul_search(query="학ꡐ", display=50, type="XML")` β†’ Search school-related administrative rules
2. `admrul_service(id="62505", type="XML")` β†’ Retrieve rule content
3. `expc_search(query="학ꡐ", display=30, type="XML")` β†’ Search legal interpretations
4. `expc_service(id="334617", type="XML")` β†’ Retrieve interpretation details

**Result:** LLM provides overview of administrative framework for schools with official agency interpretations.

---

### Trajectory 6: Comprehensive Legal Analysis
**User Query:** "I'm researching rental housing disputes. Show me the relevant law, court precedents, and administrative appeal decisions."

**Tool Calls:**
1. `eflaw_search(query="μ£Όνƒμž„λŒ€μ°¨λ³΄ν˜Έλ²•", display=50, type="XML")` β†’ Find Housing Lease Protection Act
2. `eflaw_service(id="002876", type="XML")` β†’ Retrieve full law text
3. `prec_search(query="μ£Όνƒμž„λŒ€μ°¨", display=50, type="XML")` β†’ Search housing lease precedents
4. `prec_service(id="156789", type="XML")` β†’ Retrieve key precedent
5. `decc_search(query="μ£Όνƒμž„λŒ€μ°¨", display=30, type="XML")` β†’ Search administrative appeal decisions
6. `decc_service(id="243263", type="XML")` β†’ Retrieve appeal decision

**Result:** LLM provides comprehensive legal research report covering statutory framework, judicial interpretation, and administrative precedents for rental housing disputes.

---

### Trajectory 7: Citation Network Analysis (Phase 4)
**User Query:** "What laws does Article 3 of the Building Act cite?"

**Tool Calls:**
1. `eflaw_search(query="건좕법", display=50, type="XML")` β†’ Find Building Act, get MST
2. `article_citation(mst="268611", law_name="건좕법", article=3)` β†’ Extract all citations

**Result:** LLM provides complete citation analysis showing 12 citations (8 external laws, 4 internal references) including specific article and paragraph references.

---

### Trajectory 8: AI-Powered Natural Language Search (Phase 5)
**User Query:** "What's the penalty for hit-and-run accidents?"

**Tool Calls:**
1. `aiSearch(query="λΊ‘μ†Œλ‹ˆ 처벌", search=0, display=20, type="XML")` β†’ Semantic search for hit-and-run penalties

**Result:** LLM receives full article text from relevant laws (νŠΉμ •λ²”μ£„ κ°€μ€‘μ²˜λ²Œ 등에 κ΄€ν•œ 법λ₯  제5쑰의3) with complete provisions about hit-and-run penalties, enabling comprehensive answer without needing to know specific law names.

---

### Trajectory 9: Discovering Related Laws (Phase 5)
**User Query:** "What laws are related to the Civil Code?"

**Tool Calls:**
1. `aiRltLs_search(query="민법", search=0, type="XML")` β†’ Find semantically related laws

**Result:** LLM discovers related laws like 상법 (Commercial Act), μ˜λ£Œλ²• (Medical Service Act), μ†Œμ†‘μ΄‰μ§„λ²• (Act on Special Cases Concerning Expedition of Litigation), showing connections across legal domains.

---

### Key Patterns

1. **AI Tools for Vague Queries**: Use `aiSearch` or `aiRltLs_search` FIRST when user intent is unclear or conversational
2. **Search First, Then Retrieve**: Always search to find IDs before calling service tools
3. **Use display=50-100 for Law Searches**: Ensures exact matches are found due to relevance ranking
4. **Combine Phases**: Mix Phase 1 (laws), Phase 2 (administrative rules), Phase 3 (precedents), and Phase 5 (AI search) for complete research
5. **Type Parameter**: Default is `type="JSON"`; specify `type="XML"` if your pipeline requires XML
6. **Article Numbers**: Use 6-digit format (e.g., "002000" for Article 20) when querying specific articles

## Development

### Project Structure

```
lexlink-ko-mcp/
β”œβ”€β”€ src/lexlink/
β”‚   β”œβ”€β”€ server.py        # Main MCP server with 54 tools
β”‚   β”œβ”€β”€ _helpers.py      # Shared helpers: run_search, run_service, TOOL_ANNOTATIONS
β”‚   β”œβ”€β”€ cache.py         # Intelligent per-tool TTL caching (~183 lines)
β”‚   β”œβ”€β”€ resolver.py      # Korean law name/abbreviation resolution (~225 lines)
β”‚   β”œβ”€β”€ http_server.py   # HTTP/SSE server for Kakao PlayMCP
β”‚   β”œβ”€β”€ stdio_server.py  # Stdio transport entry point
β”‚   β”œβ”€β”€ params.py        # Parameter resolution & mapping
β”‚   β”œβ”€β”€ validation.py    # Input validation
β”‚   β”œβ”€β”€ parser.py        # XML parsing utilities
β”‚   β”œβ”€β”€ ranking.py       # Relevance ranking
β”‚   β”œβ”€β”€ citation.py      # Article citation extraction (Phase 4)
β”‚   β”œβ”€β”€ client.py        # HTTP client for law.go.kr API
β”‚   β”œβ”€β”€ errors.py        # Error codes & responses
β”‚   β”œβ”€β”€ raw_logger.py    # PlayMCP traffic logging
β”‚   └── log_processor.py # Log format converter
β”œβ”€β”€ logs/playmcp/         # PlayMCP traffic logs (daily JSONL)
β”œβ”€β”€ pyproject.toml        # Project configuration
└── README.md             # This file
```

### Running Tests

```bash
# Install test dependencies
uv sync

# Run all tests
uv run pytest

# Run with coverage
uv run pytest --cov=src/lexlink --cov-report=html

# Run specific test category
uv run pytest -m unit
uv run pytest -m integration
uv run pytest -m e2e
```

### Adding New Tools

**Current Status:** 54/54 tools implemented (Phase 1-9 complete). Phase 1-5 tools validated.

For implementing additional tools from the 124+ remaining APIs:
1. Follow the pattern established in `src/lexlink/server.py`
2. Use `ctx: Context = None` parameter for MCP logging/progress
3. Use generic parser functions (`extract_items_list`, `update_items_list`)
4. Add semantic validation tests

**Tool Implementation Pattern:**
- Each tool is a decorated function with MCP schema
- Uses `ctx: Context = None` parameter for MCP context
- 2-tier OC resolution: tool arg > env var
- Generic parser functions work with any XML tag
- Comprehensive error handling with actionable hints

## Deployment

### Deploy to Kakao PlayMCP (HTTP Server)

LexLink can also be deployed as an HTTP server for platforms like Kakao PlayMCP.

> **Important:** Kakao PlayMCP does not accept port numbers in URLs.
> You must use Nginx as a reverse proxy to serve on port 80.

**Quick Start (Local Testing):**
```bash
# Run the HTTP server
OC=your_oc uv run serve

# Server starts at: http://localhost:8000/sse
```

**Production Setup:**
```
Internet β†’ Nginx (port 80) β†’ LexLink (port 8000)
```

**PlayMCP Registration:**

| Field | Value |
|-------|-------|
| **MCP Endpoint** | `http://YOUR_SERVER_IP/sse` (no port!) |
| **Authentication** | Key/Token (Header: `OC`) |

For detailed deployment instructions (AWS EC2, Nginx, systemd, HTTPS), see [docs/DEPLOYMENT_GUIDE.md](docs/DEPLOYMENT_GUIDE.md).

### PlayMCP Traffic Logging

LexLink includes built-in logging for PlayMCP traffic analysis. Logs are saved in dashboard-compatible JSONL format.

**Log Location:** `logs/playmcp/YYYY-MM-DD.jsonl`

**Log Schema:**
```json
{
  "rpc_id": "3",
  "request_id": "d8ee45eb",
  "session_id": "9ff9dc23431848a4901b4cb6326ba5bd",
  "timestamp": "2025-12-25T05:40:23.957987",
  "duration_ms": 1.52,
  "method": "tools/call",
  "tool_name": "aiSearch",
  "params": { "arguments": {"query": "λΊ‘μ†Œλ‹ˆ 처벌"} },
  "client": "PlayMCP",
  "client_version": "2025.0.0",
  "protocol_version": "2025-06-18",
  "client_ip": "220.64.111.219",
  "oc": "user_id",
  "status": "success",
  "status_code": 200,
  "result": { ... }
}
```

**Features:**
- Daily log rotation (one file per day)
- Dashboard-compatible format for filtering and analysis
- Captures request/response pairs with timing
- SSE streaming response parsing

**Converting Old Raw Logs:**
```bash
uv run python -m lexlink.log_processor input.jsonl output.jsonl
```

## Troubleshooting

### "OC parameter is required" error

**Solution:** Set your OC identifier using one of the three methods above.

### Korean characters not displaying correctly

**Solution:** Ensure your terminal supports UTF-8:
```bash
export PYTHONIOENCODING=utf-8
```

### "Timeout" errors

**Solution:** Increase timeout via environment variable:
```bash
export LEXLINK_TIMEOUT=90  # Increase from default 60s
```

### Server won't start after updating dependencies

**Solution:** Re-sync dependencies:
```bash
uv sync --reinstall
```

## Contributing

Contributions are welcome! Please:

1. Fork the repository
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
3. Write tests for new functionality
4. Ensure all tests pass (`uv run pytest`)
5. Commit changes (`git commit -m 'Add amazing feature'`)
6. Push to branch (`git push origin feature/amazing-feature`)
7. Open a Pull Request

## License

This project is open source. See LICENSE file for details.

## Acknowledgments

- **law.go.kr** - Korean National Law Information API
- **MCP** - Model Context Protocol by Anthropic
- **[korean-law-mcp](https://github.com/chrisryugj/korean-law-mcp)** - Inspiration for caching, law name resolution, and chain tools (Phase 9)

## Support

- **Issues:** [GitHub Issues](https://github.com/rabqatab/LexLink-ko-mcp/issues)
- **law.go.kr API:** [Official Documentation](http://open.law.go.kr)

---

## Changelog

### v2.1.0 - 2026-03-30
**New: Caching, Law Name Resolution, Chain Tools (Phase 9)**

- Added intelligent per-tool TTL caching (`cache.py`): search 1hr, articles 24hr, AI search 30min
- Added law name/abbreviation resolution (`resolver.py`): 52 seed aliases + dynamic learning
- Added 5 Phase 9 chain tools: `chain_full_research`, `chain_amendment_track`, `chain_dispute_prep`, `chain_law_system`, `cache_stats`
- Inspired by [korean-law-mcp](https://github.com/chrisryugj/korean-law-mcp)
- See [CHANGELOG.md](CHANGELOG.md) for full details

### v2.0.0 - 2026-03-30
**Major Release: Phase 7 Tools, JSON Default, sections Parameter**

- Added 18 new Phase 7 tools (μžμΉ˜λ²•κ·œ, μ‘°μ•½, 법령정보 μ§€μ‹λ² μ΄μŠ€, μœ„μ›νšŒ κ²°μ •λ¬Έ, μ€‘μ•™λΆ€μ²˜ 해석, νŠΉλ³„ν–‰μ •μ‹¬νŒ)
- JSON is now the default response format (was XML)
- Added `sections="summary"` parameter for case law service tools
- Refactored shared logic into `_helpers.py`
- See [CHANGELOG.md](CHANGELOG.md) for full details

### v1.5.0 - 2026-02-28
**Refactor: Remove Smithery Dependency**

- Removed `smithery` package and 8 transitive dependencies
- Simplified OC resolution to 2-tier (tool arg > env var)
- Added `stdio_server.py` entry point for stdio transport
- See [CHANGELOG.md](CHANGELOG.md) for full details

For the full changelog (v1.0.0 – v2.1.0), see [CHANGELOG.md](CHANGELOG.md).

---

**Powered by [MCP](https://modelcontextprotocol.io)**

TDQS

A3.6/5.0

Scored across 54 tools

Disambiguation2/5

Multiple tools have overlapping purposes, such as eflaw_search/law_search (differing only by effective vs announcement date), and numerous linkage/term tools (lstrm_rlt_search, lstrm_rlt_jo_search, dlytrm_rlt_search, jo_rlt_lstrm_search, ls_rlt_search) that are easily confused. The composite tools (chain_full_research, legal_resolver, check_precedent_odds) also overlap significantly in aggregating legal sources.

Naming Consistency2/5

Naming conventions are mixed: snake_case (eflaw_search, law_service), camelCase (aiSearch, aiRltLs_search), and bare noun phrases (article_citation, legal_resolver). The suffix pattern is inconsistentβ€”some use _search, some _service, and some use neither (check_precedent_odds, simplify_article).

Tool Count2/5

At 53 tools (per the list, though the server claims 54), this is far above the typical well-scoped range. The large number reflects the broad legal domain, but it makes the server unwieldy and increases the risk of misselection.

Completeness4/5

The tool set covers an extensive range of Korean legal resources: statutes, administrative rules, ordinances, treaties, precedents, constitutional decisions, interpretations, administrative appeals, committee decisions, and legal terminology, plus composite research workflows. There are minor gaps (e.g., no update/create operations, but these are read-only by design) and some redundant coverage, but overall the domain is well covered.

Maintenance

ActivitySlowing
ResponsivenessNo issues