Korean Land MCP
# π°π· Korean Land MCP (ν μ§ κ³΅κ°μ 보 MCP) β v2.0
**V-World API κΈ°λ° ν μ§Β·λμκ³ν 곡κ°μ 보 MCP μλ²**
κ΅ν κ΅ν΅λΆ V-World Open APIλ₯Ό λ°±μλλ‘ μ¬μ©ν΄, ν μ§μ΄μ(eum.go.kr)μμ μ¬λμ΄ μλμΌλ‘ νμΈνλ **μ©λμ§μΒ·μ©λμ§κ΅¬Β·μ©λꡬμΒ·μ§κ΅¬λ¨μκ³νΒ·λμκ³νμμ€Β·λ€λ₯Έ λ²λ Ή μ§μ μ¬ν**μ AIκ° μμ°μ΄λ‘ μ‘°νν μ μκ² λ§λ **Model Context Protocol** μλ²μ
λλ€.
> **korean-law-mcp** (λ²λ Ή ν
μ€νΈ) μ μ§μ΄ λλ **κ³΅κ° λ μ΄μ΄ MCP**. λ MCPλ₯Ό ν¨κ» μ°λ©΄ "μ΄ νμ§κ° μ΄λμ μνλκ° β ν΄λΉ λ²λ ΉΒ·μ‘°λ‘κ° λ¬΄μμ νμ©/μ ννλκ°" κΉμ§ μμ°μ΄ ν μ€λ‘ μ°κ²°λ©λλ€.
## β¨ v2.0 μν€ν
μ² μμΉ
1. **V-World API μ μ©**. λ€λ₯Έ μμ© APIΒ·μ€ν¬λνΒ·λͺ¨μ(mock) λ°μ΄ν° μμ.
2. **μ μ§ν μ€ν¨**. νΉμ λ μ΄μ΄μμ V-Worldκ° 500μ λμ§λ©΄ `layer_errors`μ κ·Έλλ‘ λ
ΈμΆ. μ λ κ°μ§ λ°μ΄ν°λ‘ μ±μ°μ§ μμ.
3. **korean-law-mcpμ μν λΆλ¦¬**. μ΄ MCPλ "**곡κ°μ μ΄λμ μνλκ°**"λ§ λ΅ν¨. μ‘°λ¬Έ ν΄μμ korean-law-mcpλ‘.
4. **κ΅ν κ³νλ² μ 76μ‘°β€ μ°μ μμ μλ κ°μ§**. λμ
μ§ν₯ꡬμ·보μ μ°μ§Β·μμμ보νΈκ΅¬μΒ·κ΅κ°μ°μ
λ¨μ§ λ±μ΄ νμ§μ 걸리면 `priority_delegation_hint` λ‘ "κ΅ν κ³νλ² μνλ Ή λ³ν λμ μ΄λ λ²λ Ήμ λ¨Όμ λ΄μΌ νλκ°"λ₯Ό μλ €μ€.
## π οΈ μ 곡 λꡬ (7κ°)
| λꡬ | μ€λͺ
|
|---|---|
| `resolve_parcel` | μ£Όμ/μ§λ²/PNU β νμ€νλ PNUΒ·μ§λͺ©Β·κ³΅μμ§κ°Β·νμ ꡬμΒ·WGS84 μ’ν |
| `get_zoning` | μ©λμ§μ (λμ/κ΄λ¦¬/λλ¦Ό/μμ°νκ²½) + μ©λμ§κ΅¬ (8μ’
) + μ©λꡬμ (κ°λ°μ νΒ·λμμμ°κ³΅μ) + ν μ§κ±°λνκ°κ΅¬μ |
| `get_district_plan` | μ§κ΅¬λ¨μκ³νꡬμ + κ°λ°νμνκ°μ νꡬμ |
| `get_urban_facility` | λμκ³νμμ€ 9μ’
(λλ‘Β·κ΅ν΅Β·κ³΅κ°Β·μ ν΅κ³΅κΈΒ·κ³΅κ³΅λ¬Έν체μ‘Β·λ°©μ¬Β·λ³΄κ±΄μμΒ·νκ²½κΈ°μ΄Β·κΈ°ν). `radius_m` νλΌλ―Έν°λ‘ "μ μ΄" vs "μ ν¨" κ΅¬λΆ |
| `get_other_law_designations` | 42κ° λ€λ₯Έ λ²λ Ή μ§μ λ μ΄μ΄ (λμ§Β·μ°λ¦ΌΒ·μ°μ
λ¨μ§Β·μμ§νκ²½Β·μΆμ°Β·λ¬Ένμ¬Β·μμ°κ³΅μΒ·νΉμμ§κ΅¬Β·μ£Όκ±°μ λΉΒ·μ¬ν΄Β·ν΄μΒ·ν곡). μ 76μ‘°β€ μ°μ μμ ν보 μλ νμ |
| `get_land_attributes` | μ§λͺ©(28μ’
) νμ± + κ°λ³κ³΅μμ§κ° + 건물 μ 보 |
| `analyze_parcel` | μ 6κ° λꡬλ₯Ό ν λ²μ λ³λ ¬ νΈμΆ + korean-law-mcp λ€μ λ¨κ³ ννΈ μμ± |
`discover_tools` λ ν¬ν¨λμ΄ μμ΄ λꡬ μΉ΄νλ‘κ·Έλ₯Ό μμ°μ΄λ‘ νμ κ°λ₯.
## π‘ V-World λ μ΄μ΄ 컀λ²λ¦¬μ§
- **μ©λμ§μ**: `LT_C_UQ111/112/113/114` (λμΒ·κ΄λ¦¬Β·λλ¦ΌΒ·μμ°ν경보μ μ 체)
- **μ©λμ§κ΅¬Β·κ΅¬μ**: `LT_C_UQ121~130`, `LT_C_UD801`, `LT_C_UQ162`
- **μ§κ΅¬λ¨μκ³νΒ·νκ°μ ν**: `LT_C_UPISUQ161`, `LT_C_UPISUQ171`
- **λμκ³νμμ€**: `LT_C_UPISUQ151~159`
- **λ€λ₯Έ λ²λ Ή μ§μ **: λμ§(AGRIXUE) Β· μ°λ¦Ό(UF) Β· μ°μ
λ¨μ§(WGISIE\*, DAM\*) Β· μμ§νκ²½(UM, WGISARWET) Β· μΆμ°(UM000) Β· λ¬Ένμ¬(UO) Β· μμ°κ³΅μ(WGISNP\*) Β· νΉμμ§κ΅¬(UO/UJ/UH/UB) Β· μ£Όκ±°μ λΉ(UD) Β· μ¬ν΄(UP) Β· ν΄μ(TFISMPA, WGISRE\*) Β· ν곡(AIS\*)
- **νμ§Β·κ±΄λ¬Ό**: `LP_PA_CBND_BUBUN`, `LT_C_BLDGINFO`, `A2SM_LNDPRCPS`
## π λΉ λ₯Έ μμ
### 1. ν λ°©μ μ
μ
```bash
git clone https://github.com/UrbanWatcherKr/korean-land-mcp.git
cd korean-land-mcp
npm run setup
```
`npm run setup` μ `npm install` β `npm run build` β μΈν°λν°λΈ **configure** μμΌλ‘ μ€νλ©λλ€. configure λ¨κ³μμ V-World API ν€Β·λλ©μΈμ λ¬»κ³ `.env` νμΌμ μλ μμ±ν©λλ€.
**V-World API ν€ λ°κΈ**: https://www.vworld.kr/dev/v4api.do (λ‘κ·ΈμΈ β μ€νAPI β μΈμ¦ν€ λ°κΈ, 무λ£). λ‘컬 κ°λ°μ©μ΄λ©΄ λλ©μΈμ `localhost`λ‘ λ±λ‘.
### 2. μ€μ λ§ λ€μ νκΈ°
API ν€λ₯Ό λ°κΎΈκ±°λ λλ©μΈμ μμ νκ³ μΆμΌλ©΄ μΈμ λ :
```bash
npm run configure
```
### 3. Claude Desktop / Claude Code μ λ±λ‘
configure κ° λλλ©΄ λΆμ¬λ£μ JSON λΈλ‘μ ν°λ―Έλμ μΆλ ₯ν©λλ€. λλ μλμΌλ‘:
```json
{
"mcpServers": {
"korean-land": {
"command": "node",
"args": ["/absolute/path/to/korean-land-mcp/dist/server.js"],
"env": {
"VWORLD_API_KEY": "your_real_key_here",
"VWORLD_DOMAIN": "localhost"
}
}
}
}
```
## π¬ μ¬μ© μμ
- "κ²½κΈ°λ ννμ ν¬μΉμ λ΄κΈ°λ¦¬ 680 μ©λμ§μ μλ €μ€" β `get_zoning`
- "μ΄ νμ§μ μ 76μ‘°β€ μ°μ μμ μ μ©λλμ§ μ²΄ν¬ν΄μ€" β `get_other_law_designations`
- "λ°κ²½ 50m μμ λμκ³νμμ€ μ μ΄Β·μ ν¨ μμ΄?" β `get_urban_facility({ radius_m: 50 })`
- "μ΄ μ§λ² μ 체 λΆμνκ³ korean-law λ€μ λ¨κ³ μλ €μ€" β `analyze_parcel`
## π§ korean-law-mcp μμ μν¬νλ‘μ°
```
μ¬μ©μ: "μ΄ μ§λ²μ 곡μ₯ μ§μ μ μμ΄?"
β
korean-land-mcp Β· analyze_parcel
β (μ©λμ§μ=μΌλ°κ³΅μ
, μ°μ
λ¨μ§=μμ°ν¬μΉ, μ°μ μμ=μ°μ
μ
μ§λ²)
korean-law-mcp Β· search_law("μ°μ
μ
μ§λ²")
β
korean-law-mcp Β· get_law_text(μ°μ
μ
μ§λ² μ 33μ‘°)
β
κ²°λ‘ + μλ¬Έ κ·Όκ±°
```
## ποΈ νλ‘μ νΈ κ΅¬μ‘°
```
src/
βββ server.ts # MCP stdio μνΈλ¦¬, 7κ° λꡬ λ±λ‘
βββ lib/
β βββ vworld.ts # V-World HTTP ν΄λΌμ΄μΈνΈ (5xx μ¬μλ 1ν)
β βββ overlays.ts # λ³λ ¬ λ μ΄μ΄ 쿼리 + POINT/BOX νν°
β βββ resolve.ts # μ£Όμ/μ§λ²/PNU ν΄μ
β βββ jimok.ts # μ§λͺ© μ½λ 28μ’
λ§€ν
βββ tools/
βββ resolve_parcel.ts
βββ get_zoning.ts
βββ get_district_plan.ts
βββ get_urban_facility.ts
βββ get_other_law_designations.ts
βββ get_land_attributes.ts
βββ analyze_parcel.ts
```
## π§ͺ ν
μ€νΈ
**μ λ ν
μ€νΈ** (μμ ν¨μ, API ν€ λΆνμ):
```bash
npm test
```
**Live smoke test** (μ€μ V-World νΈμΆ, `VWORLD_API_KEY` νμ):
```bash
# λ¨μΌ μ§λ² λλ²κ·Έ
npx tsx tests/live/smoke-polygon.ts "μμΈνΉλ³μ λ§ν¬κ΅¬ μ°λ¨λ 229-1"
# 3κ° ν½μ€μ² νκ· ν
μ€νΈ (μ€λ
μ· λΉκ΅)
npx tsx tests/live/smoke-fixtures.ts
# μ€λ
μ· κ°±μ (V-World λ°μ΄ν° λ³κ²½ μ)
npx tsx tests/live/smoke-fixtures.ts --update
```
Fixtures: λμ μ£Όκ±°μ§(μ°λ¨λ), λλ¦Όμ§μ+μ°μ μμ(μΈμΆλ¦¬), λ³΅ν© μ©λμ§μ+λμκ°λ°(κ°λ§€λ¦¬).
## β οΈ μλ €μ§ νκ³
- **μ κΈ°λ° νμ **: κΈ°λ³Έ 쿼리λ νμ§ μ€μ¬μ 1κ°λ₯Ό V-Worldμ λμ§λ€. νμ§ ν΄λ¦¬κ³€ κ΅μ°¨κ° μλλ―λ‘ κ²½κ³μ κ±ΈμΉ μΌμ΄μ€λ λμΉ μ μμ. `get_urban_facility` μ `radius_m` λ BOX νν°λ‘ μ΄ νκ³λ₯Ό μννμ§λ§, "μ μ΄" vs "μ ν¨" μ΅μ’
νμ μ μ¬μ©μ λλ λ΄λΉ 곡무μμ ν΄λ¦¬κ³€ κ΅μ°¨ μ¬κ²μ¦μ΄ νμ.
- **V-World λ μ΄μ΄ μ₯μ **: μΌλΆ λ μ΄μ΄κ° κ°νμ μΌλ‘ HTTP 500μ λ°νν¨. 5xx 1ν μ¬μλ νμλ μ€ν¨ μ `layer_errors` μ λ
ΈμΆλλ©°, λλ¨Έμ§ λ μ΄μ΄ κ²°κ³Όλ μ μ λ°νλλ€.
- **μ§μ체 μ‘°λ‘ λ―Έν¬ν¨**: μ΄ MCPλ κ³΅κ° λ μ΄μ΄λ§. μ§μ체 λμκ³νμ‘°λ‘ μ‘°λ¬Έμ **korean-law-mcp** λλ λ²μ μ² μμΉλ²κ· APIλ‘ λ³λ μ‘°ν.
## π λΌμ΄μ μ€
MIT License.
## π€ κΈ°μ¬
μ΄μΒ·PR νμ. μ V-World λ μ΄μ΄ μΆκ° μ `src/tools/*.ts` μ `LAYERS` λ°°μ΄μ `{ id, label }` λ§ μΆκ°νλ©΄ μλμΌλ‘ `queryOverlays` νμ΄νλΌμΈμ νΈμ
λ©λλ€.
TDQS
Scored across 8 tools
Each tool has a clearly distinct purpose: resolve_parcel for resolution, get_land_attributes for parcel details, get_zoning for zoning overlays, get_district_plan for district plans, get_urban_facility for facilities, get_other_law_designations for other laws, analyze_parcel as a composite, and discover_tools for listing. No two tools overlap in functionality.
Most tools follow a 'get_X' pattern, but three use different verbs: analyze_parcel, discover_tools, resolve_parcel. The structure is still clear and predictable, and all names use snake_case, so the inconsistency is minor.
With 8 tools, the server covers essential land information queries and includes a composite tool for one-shot analysis. This is well-scoped; each tool earns its place without being overwhelming or sparse.
The tool set covers parcel resolution, attributes, zoning, district plans, urban facilities, other law designations, and a composite with buildings. Minor gaps exist (no separate building tool, no area field), but the composite mitigates these and the domain is well-covered.