<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)
[](https://smithery.ai/server/@rabqatab/lexlink-ko-mcp)
[](https://modelcontextprotocol.io)
[](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
- **26 MCP Tools** 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)
- **100% Semantic Validation** - All 26 tools confirmed returning real law data
- **Session Configuration** - Configure once, use across all tool calls
- **Error Handling** - Actionable error messages with resolution hints
- **Korean Text Support** - Proper UTF-8 encoding for Korean characters
- **Response Formats** - HTML or XML (multiple formats supported)
## Project Status
๐ **Production Ready - Phase 5 Complete!**
| Metric | Status |
|--------|--------|
| **Tools Implemented** | 26/26 (100%) โ
|
| **Semantic Validation** | 26/26 (100%) โ
|
| **MCP Prompts** | 6/6 (100%) โ
|
| **API Coverage** | ~17% of 150+ endpoints |
| **LLM Integration** | โ
Validated (Gemini) |
| **Code Quality** | Clean, documented, tested |
| **Version** | v1.3.0 |
**Latest Achievement:** Phase 5 complete! Added AI-powered semantic search tools (aiSearch, aiRltLs_search) for natural language queries.
## Prerequisites
- **Python 3.10+**
- **Smithery API key** (optional, for deployment): Get yours at [smithery.ai/account/api-keys](https://smithery.ai/account/api-keys)
- **law.go.kr OC identifier**: Get key from [open.law.go.kr](https://open.law.go.kr)
## Quick Start
### 1. Install Dependencies
```bash
uv sync
```
### 2. Configure Your OC Identifier
Choose one of three methods:
**Option A: Session Configuration (Recommended)**
```bash
# Start dev server with OC in URL
uv run dev
# Then in Smithery UI, set oc field in session config
```
**Option B: Environment Variable**
```bash
# Copy example file
cp .env.example .env
# Edit .env and set your OC
OC=your_id_here
```
**Option C: Pass in Tool Arguments**
```python
# Override OC in each tool call
eflaw_search(query="๋ฒ๋ น๋ช
", oc="your_id")
```
### 3. Run the Server
```bash
# Development mode (with hot reload)
uv run dev
# Interactive testing with Smithery Playground
uv run playground
```
## 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!)
#### 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="XML" # Response format (XML only)
)
```
**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="XML" # Response format (XML only)
)
```
**Best for:** Finding related laws like "๋ฏผ๋ฒ" โ ์๋ฒ, ์๋ฃ๋ฒ, ์์ก์ด์ง๋ฒ
### 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
### Session Configuration Schema
Configure once in Smithery UI or URL parameters:
```python
{
"oc": "your_id", # Required: law.go.kr user ID
"http_timeout_s": 60 # Optional: HTTP timeout (5-120s, default: 60)
}
```
### Parameter Priority
When resolving the OC identifier:
1. **Tool argument** (highest priority) - `oc` parameter in tool call
2. **Session config** - Set in Smithery UI/URL
3. **Environment variable** - `OC` in .env file
## 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. Session config: Set 'oc' in Smithery settings",
"3. 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**: Always specify `type="XML"` for consistent, parseable results
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 26 tools
โ โโโ http_server.py # HTTP/SSE server for Kakao PlayMCP
โ โโโ config.py # Session configuration schema
โ โโโ 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 tests/unit/
uv run pytest tests/integration/
uv run pytest tests/e2e/
```
### Adding New Tools
**Current Status:** 26/26 tools implemented and validated (Phase 1-5 complete)
For implementing additional tools from the 124+ remaining APIs:
1. Follow the pattern established in `src/lexlink/server.py`
2. Use Context injection for session configuration
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 session config
- 3-tier parameter resolution: tool arg > session > env
- Generic parser functions work with any XML tag
- Comprehensive error handling with actionable hints
## Deployment
### Deploy to Smithery
1. Create a GitHub repository:
```bash
git init
git add .
git commit -m "Initial commit"
git remote add origin https://github.com/YOUR_USERNAME/YOUR_REPO.git
git push -u origin main
```
2. Deploy at [smithery.ai/new](https://smithery.ai/new)
3. Configure session settings in Smithery UI
### 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 [assets/DEPLOYMENT_GUIDE.md](assets/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 in session config:
```python
{
"oc": "your_id",
"http_timeout_s": 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
- **Smithery** - MCP server deployment platform
## Support
- **Issues:** [GitHub Issues](https://github.com/rabqatab/LexLink-ko-mcp/issues)
- **law.go.kr API:** [Official Documentation](http://open.law.go.kr)
---
## Changelog
### v1.3.1 - 2025-12-25
**Feature: PlayMCP Traffic Logging**
- **New Modules:**
- `raw_logger.py` - Dashboard-compatible MCP traffic logger
- `log_processor.py` - Log format converter utility
- **Implementation:**
- `RawLoggingMiddleware` in HTTP server for request/response capture
- JSONL format with merged request/response pairs
- Daily log rotation at `logs/playmcp/YYYY-MM-DD.jsonl`
- SSE streaming response parsing
- **Log Schema:**
- `request_id`, `timestamp`, `duration_ms`
- `method`, `tool`, `arguments`, `result`
- `status_code`, `client_ip`, `headers`
- **Impact:**
- Enables traffic analysis for PlayMCP deployments
- Dashboard-compatible format for monitoring
### v1.3.0 - 2025-12-24
**Feature: Phase 5 - AI-Powered Search Tools**
- **New Tools:**
- `aiSearch` - AI-powered semantic law search (Tool 25)
- `aiRltLs_search` - AI-powered related laws search (Tool 26)
- **Implementation:**
- Uses law.go.kr's intelligent search system (์ง๋ฅํ ๋ฒ๋ น๊ฒ์)
- Semantic search for natural language queries
- Returns full article text (์กฐ๋ฌธ๋ด์ฉ) for comprehensive results
- XML parsing with ranked_data for LLM consumption
- **LLM Guidance:**
- Added โญ PREFERRED TOOL hints in tool descriptions
- Added `tool-selection-guide` MCP prompt for tool selection guidance
- Tools marked as preferred for vague/unclear queries
- **Impact:**
- Tool count: 24 โ 26 tools
- MCP prompts: 5 โ 6 prompts
- Better handling of natural language legal queries
### v1.2.9 - 2025-12-09
**Fix: Case-insensitive item key matching for API inconsistency**
- **Issue:** law.go.kr API uses inconsistent XML tag casing
- `prec` search โ `<prec>` (lowercase)
- `detc` search โ `<Detc>` (capitalized)
- `expc` search โ `<Expc>` (capitalized)
- **Fix:** Made `extract_items_list`, `update_items_list`, and `slim_response` case-insensitive
### v1.2.8 - 2025-12-09
**Fix: Case-sensitive item key extraction for non-law searches**
- **Issue:** `extract_items_list()` used capitalized keys ('Prec', 'Detc', 'Expc', 'Decc') but XML parser outputs lowercase ('prec', 'detc', etc.)
- **Result:** `ranked_data` never set โ empty responses for all non-law searches
- **Fix:** Changed all 8 calls to use lowercase keys
### v1.2.7 - 2025-12-09
**Refactor: Pattern-based essential field detection**
- **Issue:** Manual field definitions per data type required maintenance for each new API
- **Solution:** Regex pattern matching for automatic field detection
- **Patterns:** `*์ผ๋ จ๋ฒํธ` (IDs), `*๋ช
ํ๊ธ/*๋ช
` (names), `*์ผ์` (dates), etc.
- **Benefit:** Works automatically for any data type without manual definitions
### v1.2.6 - 2025-12-09
**Fix: Data-type-specific essential fields for slim response**
- **Issue:** `slim_response()` used law-specific field names for all data types
- Precedent searches (`prec_search`) returned empty results
- Fields like `ํ๋ก์ผ๋ จ๋ฒํธ`, `์ฌ๊ฑด๋ช
` were filtered out because they didn't match law fields
- **Solution:**
- Implemented `essential_fields_by_type` dictionary with correct fields per data type
- Each API type (law, prec, detc, expc, decc, admrul, elaw) now has its own essential fields
### v1.2.5 - 2025-12-09
**Fix: Add ๋ฒ๋ นID to essential fields for correct parameter usage**
- **Issue:** LLM was confusing `๋ฒ๋ น์ผ๋ จ๋ฒํธ` (MST) with `๋ฒ๋ นID` in slimmed responses
- Example: Calling `eflaw_service(id="235555")` when 235555 is MST, not ๋ฒ๋ นID
- Correct usage should be `eflaw_service(mst=235555)` or `eflaw_service(id="001624")`
- **Solution:**
- Added `๋ฒ๋ นID` back to `essential_fields` in `slim_response()`
- Now LLM can see both fields and use correct parameter
- **Essential fields:** ๋ฒ๋ น๋ช
ํ๊ธ, ๋ฒ๋ น์ผ๋ จ๋ฒํธ, **๋ฒ๋ นID**, ํํ์ฐํ์ฝ๋, ์ํ์ผ์
- **Note:** If ranking not working, ensure EC2 has latest code with relevance ranking
### v1.2.4 - 2025-12-09
**Fix: Slim response mode for PlayMCP size limits**
- **Issue:** v1.2.3's truncation approach was insufficient because `ranked_data` still contained full data (~25KB per 50 results)
- **Solution:**
- Replaced `truncate_response()` with `slim_response()` function
- When `SLIM_RESPONSE=true`: removes `raw_content` entirely and keeps only essential fields in `ranked_data`
- Essential fields kept: ๋ฒ๋ น๋ช
ํ๊ธ, ๋ฒ๋ น์ผ๋ จ๋ฒํธ, ํํ์ฐํ์ฝ๋, ์ํ์ผ์
- Fields removed: ๋ฒ๋ น์ฝ์นญ๋ช
, ๊ณตํฌ์ผ์, ๊ณตํฌ๋ฒํธ, ์ ๊ฐ์ ๊ตฌ๋ถ๋ช
, ์๊ด๋ถ์ฒ์ฝ๋, ์๊ด๋ถ์ฒ๋ช
, ๋ฒ๋ น๊ตฌ๋ถ๋ช
, ๊ณต๋๋ถ๋ น์ ๋ณด, ์๋ฒํ๋ฒ์ฌ๋ถ, ๋ฒ๋ น์์ธ๋งํฌ
- **Configuration:**
- Smithery: No change needed (full response)
- PlayMCP: Set `SLIM_RESPONSE=true` in systemd service
- **Impact:**
- Response size reduced from ~50KB to ~3-5KB for 50 results
- PlayMCP works reliably without size errors
### v1.2.3 - 2025-12-08
**Fix: Response truncation for PlayMCP size limits**
- **Issue:** PlayMCP has response size limits (~50KB), causing "Tool call returned too large content part" errors
- **Solution:**
- Added `truncate_response()` helper function for optional response size limiting
- Applied truncation to all 11 search tools (eflaw_search, law_search, elaw_search, admrul_search, lnkLs_search, lnkLsOrdJo_search, lnkDep_search, prec_search, detc_search, expc_search, decc_search)
- Added `MAX_RESPONSE_SIZE` environment variable (only truncates when set)
- **Configuration:**
- Smithery: No change needed (no size limit)
- PlayMCP: Set `MAX_RESPONSE_SIZE=50000` in systemd service
- **Impact:**
- Smithery keeps full functionality (unlimited responses)
- PlayMCP avoids size errors with truncated responses + Korean message
### v1.2.2 - 2025-12-06
**Feature: HTTP/SSE Server for Kakao PlayMCP**
- **New Feature:**
- Added HTTP/SSE server support for Kakao PlayMCP deployment
- New `uv run serve` command to start HTTP server
- OCHeaderMiddleware extracts OC from HTTP headers (Key/Token auth)
- Supports both SSE (`/sse`) and Streamable HTTP (`/mcp`) transports
- **Breaking Changes:**
- Renamed `LAW_OC` environment variable to `OC` (matches official law.go.kr API naming)
- **Configuration Changes:**
- Removed `base_url` from Smithery UI config (kept as internal field only)
- Updated default timeout from 15s to 60s
- Timeout range extended to 5-120s
- **Documentation:**
- Added Kakao PlayMCP deployment guide (`assets/DEPLOYMENT_GUIDE.md`)
- Updated README with PlayMCP deployment section
- Added `http_server.py` to project structure
- **Impact:**
- LexLink can now be deployed to Kakao PlayMCP and similar HTTP-based platforms
- Each PlayMCP user provides their own OC via HTTP header (uses their own API quota)
### v1.2.1 - 2025-11-30
**Fix: Improve LLM guidance for specific article queries**
- **Issue:** LLMs were fetching entire laws (1MB+ for ์๋ณธ์์ฅ๋ฒ) instead of using `jo` parameter for specific articles like "์ 174์กฐ"
- **Solution:**
- Added **IMPORTANT** notices in `eflaw_service` and `law_service` docstrings about using `jo` parameter
- Added **BEST TOOL** guidance in `eflaw_josub` and `law_josub` docstrings
- Added practical examples: `jo="017400"` for ์ 174์กฐ, `jo="000300"` for ์ 3์กฐ
- Warned about large response sizes (400+ articles, 1MB+ responses)
- **Impact:**
- LLMs now correctly use `jo` parameter for specific article queries
- LLMs prefer `law_josub`/`eflaw_josub` for article queries
- Faster responses and cleaner output for specific article requests
### v1.2.0 - 2025-11-30
**Feature: Phase 4 - Article Citation Extraction**
- **New Tool:**
- `article_citation` - Extract legal citations from any law article (Tool 24)
- **Implementation:**
- HTML parsing approach for 100% accuracy (no LLM hallucination risk)
- CSS class-based citation type detection (sfon1-4 classes)
- MST โ lsiSeq ID mapping between XML API and HTML pages
- Zero external API costs (no LLM calls required)
- ~350ms average extraction time
- **Features:**
- Distinguishes internal vs external citations
- Extracts article, paragraph, and sub-item references
- Consolidates duplicate citations with citation counts
- Preserves raw citation text for context
- **MCP Prompts Added:**
- `extract-law-citations` - Extract and explain citations from a law article
- `analyze-citation-network` - Analyze legal citation network for a law
- **Test Coverage:**
- Unit tests: Citation module 100% coverage
- Integration tests: End-to-end extraction validated
- LLM workflow tests: Gemini 2.0 Flash validated
- **Known Limitations:**
- Range references (e.g., "์ 88์กฐ ๋ด์ง ์ 93์กฐ") return first article only
- External law names require separate search for MST lookup
- **Impact:**
- Tool count: 23 โ 24 tools
- MCP prompts: 3 โ 5 prompts
- Enables citation network analysis workflows
### v1.1.0 - 2025-11-14
**Feature: Phase 3 - Case Law & Legal Research APIs**
- **Changes:**
- Added 8 new tools for case law and legal research (15 โ 23 tools)
- `prec_search`, `prec_service` - Court precedents (ํ๋ก)
- `detc_search`, `detc_service` - Constitutional Court decisions (ํ์ฌ๊ฒฐ์ ๋ก)
- `expc_search`, `expc_service` - Legal interpretations (๋ฒ๋ นํด์๋ก)
- `decc_search`, `decc_service` - Administrative appeal decisions (ํ์ ์ฌํ๋ก)
- **Implementation:**
- Added generic parser functions (`extract_items_list`, `update_items_list`) that work with any XML tag
- Added 13 new Phase 3 parameters: `prnc_yd`, `dat_src_nm`, `ed_yd`, `reg_yd`, `expl_yd`, `dpa_yd`, `rsl_yd`, `curt`, `inq`, `rpl`, `itmno`, `cls`
- All ranking and validation functions compatible with Phase 3 tools
- Zero breaking changes to existing Phase 1 & 2 tools
- **Impact:**
- Tool count increased by 53% (15 โ 23 tools)
- API coverage increased from ~10% to ~15%
- Legal research categories expanded by 133% (3 โ 7 categories)
- All 23 tools validated and working in production
### v1.0.8 - 2025-11-13
**Fix: Complete parameter type consistency across all tools**
- **Issue:** v1.0.2 fixed 7 tools but missed 2 additional tools (`eflaw_josub`, `law_josub`), creating inconsistency where same parameters had different types across tools
- **Root Cause:**
- `eflaw_josub` and `law_josub` still had `id: Optional[str]` and `mst: Optional[str]` instead of `Union[str, int]`
- `lnkLsOrdJo_search` had `jo: Optional[int]` instead of `Union[str, int]`
- Caused validation errors when LLMs passed integers to these specific tools
- **Solution:**
- Fixed `eflaw_josub`: `id` and `mst` now accept `Union[str, int]`
- Fixed `law_josub`: `id` and `mst` now accept `Union[str, int]`
- Fixed `lnkLsOrdJo_search`: `jo` now accepts `Union[str, int]`
- **Verification:**
- All 7 tools with `id` parameter now consistently `Optional[Union[str, int]]`
- All 6 tools with `mst` parameter now consistently `Optional[Union[str, int]]`
- All 5 tools with `jo` parameter now consistently `Optional[Union[str, int]]`
- **Impact:**
- 100% parameter type consistency achieved
- No validation errors regardless of which tool LLMs choose
- Better developer experience - same parameters work identically everywhere
### v1.0.7 - 2025-11-10
**Fix: Improve search reliability and LLM guidance**
- **Issue:** LLMs often failed to find common laws like "๋ฏผ๋ฒ" (Civil Code) because:
1. Ranking fetch limit (50 results) was too small for large result sets (e.g., 77 results for "๋ฏผ๋ฒ")
2. LLMs defaulted to small `display` values (e.g., 5), missing exact matches
3. `jo` parameter rejected integers, causing validation errors and retry loops
- **Root Cause:**
- v1.0.5 ranking fetched only 50 results; "๋ฏผ๋ฒ" was beyond position 50 alphabetically
- Tool descriptions didn't guide LLMs to use larger display values
- Parameter type strictness caused UX friction
- **Solution:**
- Increased ranking fetch limit from 50 to **100 results** (API maximum)
- Updated `jo` parameter to accept `Union[str, int]` with auto-conversion
- Added guidance in tool descriptions: **"Recommend 50-100 for law searches (๋ฒ๋ น ๊ฒ์) to ensure exact matches are found"**
- **Changes:**
- All 4 search tools now fetch up to 100 results for ranking
- All 4 tools with `jo` parameter (`eflaw_service`, `law_service`, `eflaw_josub`, `law_josub`) now accept integers
- All 7 search tool descriptions updated with display recommendations
- **Impact:**
- LLMs now find "๋ฏผ๋ฒ" correctly even with small initial display values
- No more validation errors when LLMs pass article numbers as integers
- Better guidance leads to more efficient searches
### v1.0.6 - 2025-11-10
**Enhancement: Improve MCP server quality score (Smithery.ai optimization)**
- **Changes:**
- Set OC configuration default value to "test" for easier onboarding
- Added tool annotations to all 15 tools (readOnlyHint=True, destructiveHint=False, idempotentHint=True)
- Enhanced parameter descriptions in docstrings for all tools
- Implemented 3 MCP prompts for common use cases
- **Prompts Added:**
- `search-korean-law`: Search for a Korean law by name and provide a summary
- `get-law-article`: Retrieve and explain a specific article from a law
- `search-admin-rules`: Search administrative rules by keyword
- **Impact:** Expected Smithery quality score improvement from 47/100 to ~73/100 (+26 points)
- Tool annotations: +9pts
- Parameter descriptions: +12pts
- MCP prompts: +5pts
### v1.0.5 - 2025-11-10
**Fix: Improve ranking by fetching more results before ranking**
- **Issue:** v1.0.4 ranking was not working properly because it only ranked the limited results returned by the API (e.g., with `display=5`, only 5 alphabetically-ordered results were fetched). If "๋ฏผ๋ฒ" wasn't in those first 5 results, ranking couldn't help.
- **Root Cause:** Ranking logic was applied AFTER API returned limited results, so relevant matches outside the initial page were never considered.
- **Solution:**
- When ranking is enabled and `display < 50`, automatically fetch up to 50 results from API
- Apply ranking to the larger result set (50 results)
- Trim back to original requested `display` amount after ranking
- Update `numOfRows` in response to reflect actual number of results returned
- **Implementation:**
- Updated all 4 search tools: `eflaw_search`, `law_search`, `elaw_search`, `admrul_search`
- Added `original_display` tracking and `ranking_enabled` flag
- Fetch 50 results when ranking applies, then trim to requested amount
- **Examples:**
- User requests `display=5` for query "๋ฏผ๋ฒ"
- System fetches 50 results (includes "๋ฏผ๋ฒ" even if it's not in first 5 alphabetically)
- Ranking places "๋ฏผ๋ฒ" first
- System returns top 5 ranked results (now "๋ฏผ๋ฒ" appears first)
- **Impact:** Ranking now actually works - exact matches appear first regardless of alphabetical position in API results
### v1.0.4 - 2025-11-10
**Feature: Add relevance ranking to search results**
- **Issue:** law.go.kr API returns results in alphabetical order, causing irrelevant matches to appear first (e.g., searching "๋ฏผ๋ฒ" returned "๋๋ฏผ๋ฒ" first instead of exact match "๋ฏผ๋ฒ")
- **Solution:**
- Added intelligent relevance ranking that prioritizes exact matches over alphabetical ordering
- Ranking applies automatically to XML responses for keyword searches
- Results are reordered: exact match โ starts with query โ contains query โ other matches
- **Implementation:**
- Added `ranking.py` module with `rank_search_results()`, `should_apply_ranking()`, and `detect_query_language()` functions
- Added `parser.py` module for XML parsing and structured data extraction
- Updated 4 major search tools: `eflaw_search`, `law_search`, `elaw_search`, `admrul_search`
- Ranking preserves raw XML while adding `ranked_data` field for LLM consumption
- Special handling for `elaw_search`: Detects query language (Korean vs English) and ranks by matching name field
- **Examples:**
- Query "๋ฏผ๋ฒ" now returns: "๋ฏผ๋ฒ" (exact) โ "๋ฏผ๋ฒ ์ํ๋ น" (starts with) โ "๋๋ฏผ๋ฒ" (alphabetical)
- Query "insurance" ranks: "Insurance Act" โ "Insurance Business Act" โ other matches
- **Impact:** Significantly improves search relevance, reducing LLM confusion and providing better user experience
### v1.0.3 - 2025-11-10
**Fix: Clarify article number format in tool descriptions**
- **Issue:** LLMs misinterpreted the 6-digit article number format (`jo` parameter), generating "000200" for Article 20 (์ 20์กฐ) instead of correct "002000", resulting in wrong article retrieval
- **Root Cause:** Tool descriptions used ambiguous example "000200" for "Article 2", leading LLMs to incorrectly pattern-match Article 20 โ "000200"
- **Solution:**
- Added comprehensive article number format documentation with multiple examples
- Added `format_article_number()` helper function in `validation.py` for future use
- Clarified that XXXXXX format = first 4 digits (article number, zero-padded) + last 2 digits (branch article suffix)
- **Changes:**
- Updated 4 tools: `eflaw_service`, `law_service`, `eflaw_josub`, `law_josub`
- Updated `lnkLsOrdJo_search` which uses separate 4+2 digit format
- Added clear examples: "000200" (Art. 2), "002000" (Art. 20), "001502" (Art. 15-2)
- **Impact:** LLMs will now correctly format article numbers, preventing queries from returning wrong articles
### v1.0.2 - 2025-11-10
**Fix: Accept both string and integer for id/mst parameters**
- **Issue:** LLMs extract numeric values from XML responses as integers (e.g., `<๋ฒ๋ น์ผ๋ จ๋ฒํธ>188376</๋ฒ๋ น์ผ๋ จ๋ฒํธ>` โ `mst=188376`), but tools expected strings, causing Pydantic validation errors
- **Solution:** Changed parameter types to accept both strings and integers with automatic conversion
- **Changes:**
- Updated 7 tool signatures: `id: Optional[str]` โ `id: Optional[Union[str, int]]`
- Added automatic string conversion at start of each affected tool
- Applied to: `eflaw_service`, `law_service`, `eflaw_josub`, `law_josub`, `elaw_service`, `admrul_service`, `lsDelegated_service`
- **Impact:** LLMs can now pass numeric IDs as integers without validation errors
### v1.0.1 - 2025-11-10
**Fix: Remove JSON format option from all tools**
- **Issue:** LLMs were selecting JSON format, but law.go.kr API does not support JSON despite documentation (returns HTML error pages with "๋ฏธ์ ์ฒญ๋ ๋ชฉ๋ก/๋ณธ๋ฌธ" message)
- **Solution:** Removed JSON as an option from all 14 tool descriptions
- **Changes:**
- Updated `type` parameter documentation to explicitly state "JSON not supported by API"
- Added warning in module docstring about JSON format limitation
- Tool defaults remain XML (working format)
- **Impact:** Prevents LLMs from requesting JSON format and receiving error pages
### v1.0.0 - 2025-11-10
**Initial Release**
- 15 MCP tools for Korean law information access
- 6 core law APIs (eflaw/law search and retrieval)
- 9 extended APIs (English laws, administrative rules, law-ordinance linkage)
- Session configuration via Context injection
- 100% semantic validation
- Production-ready for Smithery deployment
---
**Built with [Smithery](https://smithery.ai) | Powered by [MCP](https://modelcontextprotocol.io)**