Skip to main content
Glama
syleedlabs

deal-locator-mcp

by syleedlabs
README.md
# deal-locator — 상업용 부동산 딜 올인원 MCP

**국토부 공공데이터(실거래가 · 건축물대장)를 공인중개사 실무로 바꾸는 MCP 서버.**
구·동 평단가 시세 조회, 가려진 실거래가가 *어느 건물*인지 특정, 손바뀜 이력 추적,
주소 → 구/동/번지·법정코드·필지 확인, 고객용 데이터카드 자동 제작까지 — **대화 한 줄로.**

> 국토부는 상업용 실거래가의 지번을 `소격동 8*`처럼 **마스킹**해 공개합니다. 남들이
> "어느 건물인지 모른다"에서 멈출 때, deal-locator는 건축물대장 표제부로 **역매칭해 그
> 건물을 되짚고**, 그 위에 시세 · 이력 · 콘텐츠를 얹은 올인원 도구입니다.

![Version](https://img.shields.io/badge/version-1.4.0-f59e0b)
![Author](https://img.shields.io/badge/author-DLABS-1f2937)
![Tools](https://img.shields.io/badge/MCP_도구-7종-2563eb)
![Data](https://img.shields.io/badge/데이터-국토부_·_건축HUB-0ea5e9)
![Platform](https://img.shields.io/badge/platform-Claude_Desktop_·_Claude_Code-7c3aed)
![License](https://img.shields.io/badge/license-MIT-16a34a)

> **🆕 v1.5 (2026-08)** — **당월 거래가 조회에 들어옵니다.** 이전에는 조회 창을
> *직전 달부터* 세어(`months=12` = 1~12개월 전) **이번 달 체결 거래가 통째로 빠졌습니다**
> — 실측으로 1,200억 거래(2026-08-26 체결)를 5일 뒤 조회했더니 `[NOT_FOUND]`
> "실거래 없음" 이었습니다. 이제 `months` 는 **당월 포함**으로 셉니다. 당월은
> 신고기한(계약일로부터 30일)이 안 지나 표본이 얇으므로, 그 취지의 고지가 응답에
> 함께 나갑니다(0건이어도 '거래가 없었다'는 뜻이 아닙니다).
> ⚠️ 같은 `months` 값의 구간이 한 달 최신 쪽으로 이동합니다.
>
> **v1.5.2 (2026-09)** — **"표제부없음" 오판 수정.** 실재하는 지번이 `[NOT_FOUND]`
> "번지 오타/신축/멸실 가능" 으로 나오던 문제를 고쳤습니다(실측: 서초구 방배동 910-15·
> 우면동 72-8 — 둘 다 실제로는 각각 240억·32억 정확매칭 거래가 있었습니다).
> 원인은 데이터 부재가 아니라 건축물대장 표제부 수신이 **조용히 중간에 끊긴 것**이었고,
> 그 실패가 부재와 똑같은 응답으로 나갔습니다. 세 가지를 바꿨습니다.
> ① 건축HUB 를 **https** 로 부릅니다 — 평문 HTTP 는 일부 네트워크에서 중간 장비에 끊겨
> "200 + 빈 본문" 으로 돌아옵니다(실측 http 7/10·평균 11.7초 vs https 10/10·평균 0.38초).
> ② 단건 지번 조회가 동 전체(방배동 5,432건·55페이지)를 긁지 않고 **본번 지정 조회 1회**로
> 끝납니다 — 0.8초. ③ **조회 실패와 부재를 구분**합니다. 표제부를 못 받으면 `[NOT_FOUND]`
> 가 아니라 외부 API 오류로 나가므로, 이 응답을 근거로 부재를 단정하지 마세요.
>
> **v1.5.1 (2026-09)** — **fastmcp 4 호환 수정.** 플러그인 설치(`uvx --from git+…`)가 fastmcp 4.0 을
> 받으면서 `fastmcp.tools.tool` 경로가 사라져 **서버가 켜지지 않던 문제**를 고쳤습니다(fastmcp `<4` 상한 포함).
> 이미 설치한 경우 Claude Code 재시작 후에도 안 뜨면 `uv cache clean deal-locator-mcp` 를 한 번 실행하세요.
>
> **v1.4** — **카드 신뢰도 게이트**: `deal_card_create` 가 추정매칭
> 이하(신뢰도 0.90 미만)는 `LOW_CONFIDENCE` 로 멈춥니다. 카드 PNG 는 대화를 떠나
> 고객 손에 가는데, 추정매칭은 *동일 스펙 옆 건물일 수 있는* 상태이기 때문입니다.
> 근거(`match_explain`) 확인 후 `allow_estimated=true` 로만 발행되며, 발행된 카드에는
> 신뢰도 배지가 그대로 찍힙니다.
>
> **v1.3** — ① 매칭 엔진 v2: 지분거래 비율매칭·자릿수 프리필터 등으로 마스킹 지번
> 복원율 **84%**(서울 25개 구 전수, 오매칭 0) ② 새 도구
> [`deals_export`](#7-deals_export--실거래-csv-내보내기): 연월 지정 서울 전역 실거래를
> **지번 복원된 CSV**로 다운로드(2006~, 장기간은 연도별 파일) ③ 건축HUB API 응답
> 형식 변경 대응.

---

## 목차

**처음이라면** → [1분 요약](#1분-요약--이게-뭔가요) · [다루는 범위](#다루는-범위-v1) · [설치](#설치) · [도구 7종](#도구-7종)
**쓰다가 막히면** → [매칭 신뢰도](#매칭-신뢰도는-반드시-함께-읽으세요) · [FAQ](#faq) · [한계 · 주의](#한계--주의)

<details>
<summary>전체 목차 펼치기</summary>

- [1분 요약 — 이게 뭔가요?](#1분-요약--이게-뭔가요)
- [다루는 범위 (v1)](#다루는-범위-v1)
- [무엇이 들어있나요](#무엇이-들어있나요)
- [설치](#설치)
- [도구 7종](#도구-7종)
  - [1. `resolve_address` — 주소·필지 확인](#1-resolve_address--주소필지-확인)
  - [2. `deal_card_search` — 매물 종합 확인](#2-deal_card_search--매물-종합-확인)
  - [3. `deal_history` — 실거래가 손바뀜 이력 확인](#3-deal_history--실거래가-손바뀜-이력-확인)
  - [4. `area_scan` — 구/동 실거래가 최신 시세 확인](#4-area_scan--구동-실거래가-최신-시세-확인)
  - [5. `match_explain` — 매칭 근거](#5-match_explain--매칭-근거)
    - [매칭 신뢰도는 반드시 함께 읽으세요](#매칭-신뢰도는-반드시-함께-읽으세요)
  - [6. `deal_card_create` — 데이터카드](#6-deal_card_create--데이터카드)
  - [7. `deals_export` — 실거래 CSV 내보내기](#7-deals_export--실거래-csv-내보내기)
- [FAQ](#faq)
- [저장소 구조](#저장소-구조)
- [한계 · 주의](#한계--주의)
- [고지](#고지)
- [Star History](#star-history)
- [라이선스](#라이선스)

</details>

---

## 1분 요약 — 이게 뭔가요?

국토교통부 상업업무용 실거래가는 일반건물의 지번을 `소격동 8*` 처럼 가려서 공개합니다.
그래서 "이 건물이 얼마에 팔렸나"를 확인하려면 대장을 일일이 대조해야 했습니다.

이 서버는 **건축물대장 표제부(건축년도 · 연면적 · 대지면적)** 와 **부속지번 대장**으로
역매칭해 그 필지를 특정합니다.

```
소격동 8*  ·  216억 3,842만원  ·  대지 361㎡ / 연면적 356.18㎡ / 1981년
      ↓  표제부 3개 값이 정확히 일치하는 필지는 하나뿐
소격동 86  (북촌로5길 76)  —  정확매칭 0.97
```

모든 수치는 공공데이터포털(data.go.kr) 공식 API 실측값입니다. 결과가 없으면
`[NOT_FOUND]` 를 반환합니다 — **AI가 수치를 지어내지 못하도록** 설계했습니다.

---

## 다루는 범위 (v1)

> **📌 꼭 확인하세요.**

현재는 **통건물(한 필지 위 건물 한 채) 상업용건물 매매**를 다룹니다.
여기서 나오는 시세·평단가는 전부 이 통건물 기준이며,
앞으로 **토지 · 공장 · 도로** 등 다른 물건 종류로 취급 범위를 넓혀 나갈 예정입니다.

| 구분 | 물건 종류 |
|---|---|
| ✅ 지금 취급 | **통건물(유형 '일반') 상업용건물** 매매 |
| ⏳ 확장 예정 | 토지 · 공장 · 도로 등 |

> ※ 집합(구분상가) 거래는 현재 제외됩니다.

---

## 무엇이 들어있나요

| 구성 | 내용 |
|---|---|
| 조회 도구 5종 | 지번 · 이력 · 지역 스캔 · 매칭 근거 — 전부 읽기 전용 |
| 카드 도구 1종 | 조회 결과를 데이터카드 PNG 1장으로 (고객 제시 · SNS용) |
| 매칭 엔진 | 표제부 역매칭 + 부속지번 재앵커, 6단계 신뢰도 파이프라인(지분거래 비율매칭 포함) · 표제부 완전수신 보장 |
| 캐시 | 15분 · 128건 — 같은 지번 재조회는 API를 다시 때리지 않습니다 |

---

## 설치

> **한눈에** — ① 인증키 발급 → ② uv 설치 → ③ 플러그인(권장) 또는 Desktop 등록. 여기까지가 필수입니다.
> ④ 카드 기능 · ⑤ 프리워밍은 선택이니, 급하면 ③까지만 하고 바로 조회하세요.

### 1. 인증키 발급 (필수)

[공공데이터포털](https://www.data.go.kr) 에서 아래 2개를 활용신청하고 **디코딩 인증키**를 받습니다.

- 국토교통부_상업업무용 부동산 매매 신고 자료
- 건축HUB_건축물대장정보 서비스

승인까지 보통 몇 분~1시간 걸립니다.

### 2. uv 설치 (필수)

```bash
curl -LsSf https://astral.sh/uv/install.sh | sh
```

### 3-A. 플러그인으로 설치 (권장 — `/명령어`까지 함께 들어옵니다)

플러그인으로 깔면 MCP 도구 7개와 **슬래시 명령 7개**가 한 번에 붙습니다.

먼저 인증키를 홈 폴더에 파일 하나로 둡니다. 터미널에서:

```bash
echo 'DEAL_LOCATOR_SERVICE_KEY=발급받은_디코딩_인증키' > ~/.deal-locator.env
chmod 600 ~/.deal-locator.env
```

그다음 Claude 에서:

```
/plugin marketplace add syleedlabs/deal-locator-mcp
/plugin install deal-locator@dlabs
```

재시작하면 아래 명령을 바로 쓸 수 있습니다.

| 명령 | 하는 일 |
| --- | --- |
| `/area-scan` | 구·동 통건물 시세와 분기 추이 |
| `/deal-card` | 지번 실거래 한 건 조회 |
| `/deal-history` | 그 지번의 손바뀜 이력 |
| `/match-explain` | 왜 이 건물로 판단했는지 근거 |
| `/resolve-address` | 주소·필지 구성 확인 |
| `/deal-card-image` | 데이터카드 PNG 만들기 |
| `/deals-export` | 연월 지정 서울 전역 실거래 CSV 다운로드 |

> 인증키 파일은 `~/.deal-locator.env` → `~/.config/deal-locator/.env` 순으로 찾습니다.
> 프로젝트 폴더에 `.env` 가 있으면 그쪽이 우선입니다.

### 3-B. Claude Desktop 에 직접 등록 (도구만)

설정 → 개발자 → 설정 편집 → `claude_desktop_config.json`

```json
{
  "mcpServers": {
    "deal-locator": {
      "command": "uvx",
      "args": ["--from", "git+https://github.com/syleedlabs/deal-locator-mcp", "deal-locator-mcp"],
      "env": {
        "DEAL_LOCATOR_SERVICE_KEY": "발급받은_디코딩_인증키"
      }
    }
  }
}
```

Claude Desktop 을 재시작하면 도구 7개가 잡힙니다.

> ⚠️ **이 설정 파일에는 인증키가 평문으로 들어갑니다.** 화면 공유·스크린샷·
> 원격지원 때 노출되지 않게 주의하세요. 키가 유출된 것 같으면 공공데이터포털에서
> 즉시 재발급하면 됩니다. 파일에 키를 두기 싫으면 아래 FAQ 의 `.env` 방식을 쓰세요.

### 4. 카드 기능을 쓰려면 (선택)

카드는 브라우저 엔진으로 이미지를 그립니다. 최초 1회만:

```bash
uvx --from git+https://github.com/syleedlabs/deal-locator-mcp playwright install chromium
```

건너뛰어도 조회 도구 5개는 정상 동작합니다.

### 5. 첫 조회를 빠르게 (선택 — 프리워밍)

구·동을 **처음** 조회하면 국토부 API 를 12개월치 받아오느라 20~35초 걸립니다
(한 번 받은 구는 이후 즉시 응답합니다). 미리 채워두려면:

```bash
# 서울 25구 · 12개월치 캐시 채우기 (API 300회, 몇 분 소요 — 한 번만)
uvx --from git+https://github.com/syleedlabs/deal-locator-mcp deal-locator-warm

# 자주 보는 구만:  deal-locator-warm --gus 강남구 성동구 마포구
```

인증키는 조회와 같은 `~/.deal-locator.env` 를 씁니다. 중간에 끊겨도 다시 실행하면
남은 것만 이어서 받습니다. 실거래는 매달 갱신되니, 원하면 월 1회쯤 다시 돌리세요.

---

## 도구 7종

| 도구 | 하는 일 | 이렇게 물어보세요 |
|---|---|---|
| `resolve_address` | 주소 → 구/동/번지 + 법정코드, 필지 구성 확인 | "소격동 86 주소 확인해줘" |
| `deal_card_search` | 지번 하나의 최신 실거래 종합 | "소격동 86 실거래가 알려줘" |
| `deal_history` | 그 지번의 매칭 실거래 이력 전체 | "이 건물 거래 이력 다 보여줘" |
| `area_scan` | 동 단위 평당가 구간 스캔 (지번 몰라도 조회) | "성수동1가 평당 4.5~5.5억 거래" |
| `match_explain` | 매칭 근거 공개 — 표제부값 vs 거래행값 대조 | "이 매칭 왜 이렇게 나왔어?" |
| `deal_card_create` | 데이터카드 PNG 1장 생성 | "이 건으로 카드 만들어줘" |
| `deals_export` | 연월 지정 서울 전역 실거래 CSV 다운로드 | "2026년 7월 실거래 CSV로 뽑아줘" |

앞의 5개는 **읽기 전용**입니다. 파일을 만드는 도구는 `deal_card_create`(카드 PNG)와
`deals_export`(CSV) 둘뿐이고, 외부에 무언가를 보내지 않습니다.

각 도구를 **사용방법 → 결과물 → 설명** 순으로 정리한 실행 예시입니다.
값은 전부 공식 API 실측값이며, 표시는 가독성을 위해 정리한 것입니다.

---

## 1. `resolve_address` — 주소·필지 확인

**사용방법**

```
/resolve-address 종로구 소격동 86
```

**결과물**

```
종로구 소격동 86   ·   법정코드 11110-14200
필지 구성: 단일 (부속지번 없음)
```

**설명**

- 다른 조회가 막히기 전에 주소가 제대로 잡히는지 · 합필/부속지번이 있는지 먼저 확인합니다.
- `조회실패` 는 '단일 필지'가 아니라 **미확인**이니 구분해서 읽으세요.

---

## 2. `deal_card_search` — 매물 종합 확인

**사용방법**

```
/deal-card 종로구 소격동 86
```

**결과물**

```
216억 3,842만원    ·    2026-05-14    ·    정확매칭 0.97
대지 361㎡ (109.2평) · 연면적 356.18㎡ (107.7평) · 1981년 준공
대지 평단가 1억 9,815만원/평 · 제1종일반주거 · 매도 법인 → 매수 법인
북촌로5길 76 (소격동)
```

**설명**

- 지번 하나의 최신 실거래를 종합카드 1콜로. 신뢰도(정확매칭 0.97)를 함께 읽으세요.

---

## 3. `deal_history` — 실거래가 손바뀜 이력 확인

**사용방법**

```
/deal-history 성수동1가 685-442
```

**결과물**

```
[성동구 성수동1가 685-442] 매칭 실거래 이력 · 최근 24개월
■ 2026-06-18    57억 5,000만원    정확매칭 0.97    법인 → 법인
■ 2025-11-27    55억 3,000만원    정확매칭 0.97    법인 → 법인
→ 약 7개월 만에 재거래, +2억 2,000만원 (+4.0%)
   대지 136㎡(41.1평) · 연면적 209.34㎡(63.3평) · 1990년 · 제2종일반주거
※ 해제신고 6건 제외
```

**설명**

- 한 지번의 손바뀜을 최신순으로 — 재거래·가격 추이를 한눈에. 건별 신뢰도가 다를 수 있습니다.

---

## 4. `area_scan` — 구/동 실거래가 최신 시세 확인

**사용방법**

```
/area-scan 성수동1가        # 동: 시세 통계 + 개별 거래
/area-scan 성동구           # 구: 구 전체 통계 + 동별 평단가 순위
```

**결과물** — 동을 조회한 최근 한 분기(3개월) 예시입니다.

```
[성동구 성수동1가] 최근 3개월(2026 Q2) · 통건물 12건
대지 평단가    평균 1억 6,028만원 · 중앙값 1억 6,136만원 (p25 1.45억 ~ p75 2.09억)
연면적 평단가   평균 7,770만원 · 중앙값 8,530만원
표본   동 전체 14건 → 집합(구분상가) 2건 제외 · 해제 2건 제외 → 통건물 12건 집계
```

**설명**

- 동을 주면 시세 통계와 개별 거래를, 구를 주면 구 전체 통계와 동별 평단가 순위를 냅니다.
- 동 평균 시세는 국토부 원본 전수에 가까운 `stats` 로 답합니다.
- `coverage`(모수 분해)를 함께 봐야 표본이 대표성을 갖는지 판단할 수 있습니다.

---

## 5. `match_explain` — 매칭 근거

**사용방법**

```
/match-explain 영등포동8가 34-20
```

**결과물**

```
[영등포동8가 34-20] 매칭 근거 · 정확매칭 0.90 (stage2)
거래 4억 4,000만원 · 2026-05-06   (지번 마스킹 '영등포동8가 3*')

  표제부(앵커)   vs   거래행
  연면적    31.21㎡   =   31.21㎡     ✓ 일치
  건축년도   1985     =   1985        ✓ 일치
  대지면적   21.78㎡   vs   22.8㎡     Δ 1.02㎡ (신고 반올림 오차 수준)
→ 3속성 중 2개 정확 일치 + 대지면적만 근소차 → 'stage2 강등'이나 라벨은 '정확매칭'
```

**설명**

- 국토부 실거래가는 지번을 `영등포동8가 3*`처럼 **마스킹**해 공개합니다. 이 도구들은 건축물대장
  표제부(건축년도·연면적·대지면적)로 **역매칭**해 '이 건물'이라고 특정하는데, **매칭 신뢰도**는
  그 특정이 얼마나 확실한지를 뜻합니다.
- `추정매칭`이면 **같은 스펙의 옆 건물**일 수 있고, 그 값을 고객·보고서에 "이 건물 실거래가"로
  인용하면 **엉뚱한 건물 가격을 대는 오류**가 됩니다.
- `match_explain`은 어떤 표제부 값으로 어떻게 특정했는지(앵커 vs 거래행)를 대조해, 인용 전에
  그 위험을 직접 판단하게 합니다.

### 매칭 신뢰도는 반드시 함께 읽으세요

| 표기 | 뜻 |
|---|---|
| 확정 / 정확매칭 | 표제부 값이 정확히 일치 — 사실상 그 필지 |
| 추정매칭 | 유사 스펙으로 좁힌 것 — **동일 스펙 인접 건물일 수 있습니다** |
| 인접후보 | 후보 수준 — 확인 없이 인용하지 마세요 |

`추정매칭` 이하를 고객에게 제시하기 전에 `match_explain` 으로 근거를 확인하세요.

---

## 6. `deal_card_create` — 데이터카드

건물 사진 위에 실측 수치를 얹은 4:5 카드(2160×2700)를 만듭니다. 홍보 문구는
들어가지 않습니다 — 카드의 모든 글자가 실측값이거나 고정 라벨입니다.

**사용방법**

```
/deal-card-image 영등포구 영등포동8가 34-20 [건물사진]
```

> 직접 호출: `deal_card_create(address="영등포구 영등포동8가 34-20", photo="~/사진.jpg")`

**결과물** — 건물 사진(입력)에 실측 수치를 얹어 카드(출력)를 만듭니다.

<table>
<tr>
<td align="center"><b>건물 사진 (입력)</b></td>
<td align="center"><b>데이터카드 (결과물)</b></td>
</tr>
<tr>
<td><img src="assets/deal-card-create-input.png" width="280" alt="영등포동8가 34-20 건물 사진(입력)"></td>
<td><img src="assets/deal-card-create-example.png" width="280" alt="영등포동8가 34-20 데이터카드(결과물)"></td>
</tr>
</table>

> 영등포구 영등포동8가 34-20 · 거래일 2026-05-06 · 매매 4.4억 · 토지 7평(평단가 6,377만원/평) · 연면적 9평 · 준공업 · 1985년 준공 · 정확매칭 0.90.

**설명**

**멈추는 지점이 2곳 있습니다** — 둘 다 실패가 아니라 **사람이 결정할 상태**라
재시도로 뚫리지 않습니다.

- **`LOW_CONFIDENCE` — 추정매칭 이하(신뢰도 0.90 미만)는 기본적으로 만들지 않습니다.**
  카드는 대화를 떠나 고객 손에 가는데, `추정매칭`은 정의상 *동일 스펙 옆 건물일 수
  있는* 상태입니다 — 옆 건물 실거래가가 이 건물 값으로 박힌 이미지가 돌아다닐 수
  있습니다. `match_explain` 으로 근거를 확인한 뒤, 그래도 발행하기로 했다면
  **`allow_estimated=true`** 로 다시 부르세요.
- **`PHOTO_MISSING` — 건물 사진이 없으면 만들지 않습니다.** 사진 없이 만들면 회색 판이
  나가고 결국 다시 만들게 되기 때문입니다. 사진 경로를 주고 다시 부르거나, 그대로
  진행하려면 `allow_no_photo=true` 를 주세요.

**그 밖에**

- **매칭 신뢰도가 카드에 배지로 표기됩니다.** 위 게이트를 통과해 발행되는 카드에도
  근거가 항상 따라다녀야 한다고 봤습니다(`추정매칭` 이하는 색으로 구분). 즉 신뢰도는
  **차단(게이트) + 각인(배지)** 두 겹으로 다룹니다.
- 저장 위치는 `~/deal-locator-cards/<날짜>/` 입니다 (`DEAL_LOCATOR_CARD_DIR` 로 변경 가능).

---

## 7. `deals_export` — 실거래 CSV 내보내기

연월(YYYYMM)을 지정해 **서울 전역(또는 한 구)의 상업업무용 통건물 실거래를, 마스킹
지번을 역매칭으로 복원한 CSV**로 내려받습니다. DB·분석 파이프라인에 넣을 **원천
데이터**를 만드는 도구입니다 — 화면 시세 조회는 `area_scan` 이 담당합니다.

**사용방법**

```
/deals-export 202607                → 서울 25개 구 전체, 2026년 7월, 지번 복원(기본)
/deals-export 202601-202606 강남구  → 강남구 상반기(범위는 최대 24개월)
/deals-export 202607 원본           → 역매칭 없이 국토부 원본 그대로(수십 초)
```

> 직접 호출: `deals_export(year_month="202607")`,
> 범위·구 한정은 `deals_export(year_month="202601", year_month_to="202606", gu="강남구")`

**결과물** — 고정 폴더 `~/deal-locator-exports/` 에 **복원본 + 미복원본 2파일**
(utf-8-sig — 엑셀에서 바로 열립니다. `DEAL_LOCATOR_EXPORT_DIR` 로 폴더 변경 가능.
날짜 하위폴더가 없어 파이프라인이 경로를 하드코딩해도 되고, 같은 연월 재실행은 같은
파일을 덮어씁니다 — 멱등)

```
■ 실거래 CSV 내보내기 — 서울전역 202607 (통건물)
  복원본: ~/deal-locator-exports/실거래_통건물_서울전역_202607_복원.csv — 171건 (정확매칭 139건 · 추정매칭 32건)
  미복원본: ~/deal-locator-exports/실거래_통건물_서울전역_202607_미복원.csv — 31건 (금액·면적은 실측, 지번만 미확정)
  전체 202건 · 해제신고 8건 포함(해제사유발생일 컬럼으로 구분)
  ※ 집합(구분상가) 588건은 취급 범위 밖이라 제외(v1)
  구별 상위: 중구 27건 · 종로구 22건 · 강남구 11건 · …
```

### 긴 기간(예: 10년치)을 요청하면 어떻게 처리되나요

`/deals-export 2016년부터 강남구`, `최근 10년`, `2006년부터 전체` 처럼 **24개월을 넘는
기간**을 요청하면, 한 번에 통짜로 받는 게 아니라 **연 단위로 나눠 여러 번 호출하고
연도별 파일로 저장**합니다. 예를 들어 "2016~2025 강남구"는 아래처럼 진행됩니다.

```
2016년 → 실거래_통건물_강남구_2016_복원.csv (+ _미복원)
2017년 → …_2017_복원.csv
  ⋮   (한 해 끝날 때마다 그 해 복원율을 한 줄씩 보고)
2025년 → …_2025_복원.csv
─────────────────────────────────
마무리: 연도별 복원율 표 + 생성된 파일 목록
```

**왜 통짜가 아니라 연 단위로 쪼개나요 (기술적 이유)**

- **도구 한 번 호출은 최대 24개월**입니다. 한 호출이 유한 시간 안에 끝나야 MCP
  클라이언트(예: Claude Desktop)가 응답을 기다리다 **타임아웃으로 끊는 사고**를 막을 수
  있고, 도중 실패해도 그 구간만 다시 받으면 되기 때문입니다(통짜 호출은 몇 시간째
  실패하면 전부 날아갑니다).
- 그래서 **연 단위(1~12월) 청크**로 끊습니다 — 청크 경계가 **연도별 파일과 1:1로
  맞아떨어져** DB 파티션·증분 적재에 그대로 쓰기 좋습니다.
- **첫 해만 오래 걸리고 이후는 빠릅니다.** 역매칭이 쓰는 **건축물대장은 연도와 무관한
  '현재' 대장**이라, 첫 해를 처리하며 서울 전 동의 표제부를 받아 캐시에 굳히면 그다음
  해들은 **거래 조회만** 하면 됩니다. 즉 무거운 건 최초 표제부 수신 1회뿐입니다.
- **이미 받은 해는 다시 긁지 않습니다(멱등 재개).** 완결된 과거 연도는 파일이 있으면
  건너뛰고, **현재 진행 중인 해만 매번 다시 받아** 새로 공개된 월을 반영합니다
  (현재 해도 `_YYYY_` 파일명으로 고정돼 매달 같은 파일을 덮어씁니다 — 겹치는 파일이
  쌓여 파이프라인이 중복 적재하는 일을 막습니다).

**그래서 알아둘 것**

- **첫 전체 백필은 오래 걸립니다.** 서울 전역 장기이면 표제부 최초 수신 때문에 수십 분
  이상 걸릴 수 있어, 시작 전에 안내합니다. 급하면 특정 구부터 받거나 `원본`(match=false,
  역매칭 생략)으로 빠르게 받을 수 있습니다.
- **과거로 갈수록 지번 복원율이 떨어집니다** — 위에서 설명한 표제부 '현재 스냅샷' 한계
  때문입니다(최근 ~84% → 10년 전 ~79% → 20년 전 ~16%). 연도별 복원율 표를 함께
  드리니, 어느 연도부터 지번 레이어가 촘촘해지는지 보고 판단하세요. **점(지번) 레이어는
  최근·`정확매칭` 위주로, 과거·미복원·추정은 동 단위 집계 레이어로** 쓰는 걸 권합니다.

**파일 구성**

- **복원본(`…_복원.csv`)** — 지번이 특정된 거래. 국토부 원본 컬럼(마스킹 지번 포함)은
  그대로 두고 **`복원지번` · `대지위치_표제부` · `도로명대지위치_표제부` · `매칭단계` ·
  `매칭신뢰도`(정확매칭/추정매칭)** 컬럼을 추가합니다. 추정매칭은 동일 스펙 인접
  건물일 가능성이 있으니 하류에서 `매칭신뢰도` 로 필터하세요.
- **미복원본(`…_미복원.csv`)** — 지번 특정에 실패한 거래(`역매칭실패사유` 포함).
  **금액·면적은 실측값**이므로 버리지 말고 용도에 맞게 쓰세요. 0건이면 파일을 만들지
  않습니다.

**알아둘 것**

- **통건물(유형='일반')만 담깁니다.** 집합(구분상가)은 취급 범위 밖(v1)이라 빠지고
  제외 건수만 보고합니다.
- **해제신고 거래는 행으로 남습니다.** 데이터 다운로드가 행을 지우면 원본과 어긋나기
  때문입니다 — 시세 분석 전에 `해제사유발생일` 컬럼이 채워진 행을 제외하세요.
- **역매칭은 동별 건축물대장 전체를 받습니다** — 콜드 캐시면 수 분~수십 분, 한 번
  받은 뒤에는 수 분 안에 끝납니다. 빠르게 원본만 필요하면 `match=false`(`원본`).
- **조회 창(`months`)은 당월부터 셉니다** (v1.5.0~). `months=12` 면 '이번 달 포함
  최근 12개월'입니다. v1.4.0 까지는 직전 달부터 세어 **당월 거래가 통째로 안 보였습니다**
  — 실측으로 1,200억 거래가 체결 5일 뒤에도 `[NOT_FOUND]` 였습니다.
- 당월은 신고기한(계약일로부터 30일)이 안 지나 **표본이 얇습니다** — 지금 0건이어도
  '거래가 없었다'는 뜻이 아닙니다. 조회 결과에 그 취지의 고지가 함께 나갑니다.
- 최근 월은 신고 지연(신고기한 30일)으로 데이터가 아직 없을 수 있습니다 — 이때는
  `[NOT_FOUND]` 로 답하며 수치를 지어내지 않습니다.
- **확보 가능 기간은 2006년 1월 ~ 현재(약 20년)입니다.** 그 이전은 원천 데이터가
  없습니다(아래 FAQ). "2006년부터", "최근 10년", "전체" 같은 장기 요청은 스킬이
  **연 단위 파일**로 나눠 확보합니다.
- **장기 데이터는 과거로 갈수록 지번 복원율이 떨어집니다** — 표제부가 '현재 스냅샷'이라
  그 사이 재건축된 과거 건물은 못 맞춥니다(실측: 최근 ~84%, 10년 전 ~79%, 20년 전 ~16%).
  히스토리 맵을 만든다면 **점(지번) 레이어에는 `매칭신뢰도=정확매칭`만 쓰고, 미복원·
  추정매칭은 동(洞) 단위 집계 레이어로** 쓰는 걸 권합니다(금액·면적·법정동은 전 기간 실측).

## FAQ

**Q. 아파트도 되나요?**
> 아니요. **서울 · 상업업무용 · 매매**만 다룹니다(v1). 아파트 · 오피스텔 · 단독다가구 · 토지 · 전월세는 범위 밖입니다.

**Q. 첫 조회가 너무 느립니다.**
> 건축물대장 전체를 불러오기 때문에 수십 초~수 분 걸립니다. 이후 15분간 캐시되어 같은 구 조회는 즉시 나옵니다.

**Q. 실거래가는 몇 년치까지 받을 수 있나요?**
> **2006년 1월 ~ 현재, 약 20년치**입니다. **실거래가 신고 의무화가 2006년 1월 1일 시행**돼, 그 이전 거래는 신고 자체가 없어 국토부에 원천 데이터가 존재하지 않습니다(도구 하한도 2006 — `200512`는 `PARSE_ERROR`로 거부). 이 도구가 다루는 상업·업무용 통건물 매매도 2006년부터 제공됩니다.
>
> 다만 이건 *원천 데이터*가 20년치라는 뜻이고, **마스킹 지번 복원(역매칭)은 최근일수록 강하고 과거로 갈수록 급락**합니다(최근 ~84% → 10년 전 ~79% → 20년 전 ~16%). 표제부(건축물대장)가 '현재 스냅샷'이라, 그 사이 재건축·신축된 과거 건물은 현재 대장과 스펙이 안 맞기 때문입니다. 그래서 장기 히스토리 맵을 만든다면 **지번 단위 레이어는 최근 구간에서 촘촘**하고, 과거 구간은 **동 단위 집계(거래밀도·평단가 추이)** 로 쓰는 게 맞습니다 — 미복원 건도 금액·면적·법정동은 전 기간 실측이라 동 레이어엔 온전히 활용됩니다.
>
> 2006년 이전 시계열까지 필요하면 실거래가가 아닌 다른 소스(감정원 시세지수, 공시지가 이력 등)를 별도 레이어로 붙이는 방법을 검토하세요.

**Q. 계약 취소된 거래도 포함되나요?**
> 아니요. **해제신고 건은 집계에서 제외**하고 그 건수를 알려줍니다.

**Q. 지번이 특정되지 않는 거래가 있습니다.**
> 표제부와 일치하는 필지를 못 찾은 경우입니다. `area_scan` 결과에 `마스킹 미복원` 으로 표기되며, 그 건의 **금액·면적은 실측값**이지만 주소는 확정된 것이 아닙니다.

**Q. 인증키를 설정 파일에 넣기 싫습니다.**
> 실행 폴더(또는 그 상위 1단계)에 `.env` 를 두면 자동으로 읽습니다(`.env.example` 참고). `.env` 는 절대 커밋하지 마세요.
>
> 보안상 **`DEAL_LOCATOR_*` 와 `DATA_GO_KR_API_KEY` 만 읽습니다.** `.env` 의 다른 줄은 무시합니다 — 남의 프로젝트 폴더에서 서버를 띄웠을 때 그쪽 설정(프록시 등)이 섞여 들어와 요청이 엉뚱한 서버를 경유하는 일을 막기 위함입니다.
>
> 저장·캐시 폴더 설정(`DEAL_LOCATOR_CARD_DIR` · `DEAL_LOCATOR_EXPORT_DIR` · `DEAL_LOCATOR_CACHE_DIR`)은 **실행 폴더의 `.env` 에서는 읽지 않습니다.** 환경변수, `DEAL_LOCATOR_ENV_FILE` 로 지정한 파일, 홈 설정 파일(`~/.deal-locator.env` · `~/.config/deal-locator/.env`)에서만 받습니다 — 남이 만든 폴더의 `.env` 가 캐시 폴더를 바꿔, 조작된 데이터를 실측값처럼 읽게 만드는 일을 막기 위함입니다.

**Q. 카드 만들 때 임의 파일이 읽히지 않나요?**
> `photo` 는 **파일 내용으로 이미지 여부를 판별**합니다(PNG·JPEG·GIF·WebP). 이미지가 아니면 렌더하지 않고 멈춥니다. 카드 렌더는 JavaScript 를 끈 상태로 돌고 모든 네트워크 요청이 차단되므로, 카드 값에 스크립트가 섞여도 실행되지 않고 외부로 나가지도 않습니다.

---

## 저장소 구조

```
deal-locator-mcp/
├─ src/deal_locator/
│  ├─ server.py           MCP 서버 — 도구 7종 정의 · 구조화 출력
│  ├─ core/               매칭 엔진 (표제부 역매칭 · 부속지번 · 파이프라인)
│  └─ render/             데이터카드 렌더 (템플릿 + Pretendard 폰트)
├─ tests/                 114개
├─ server.json            MCP 레지스트리 메타데이터
└─ .env.example
```

---

## 한계 · 주의

이 도구가 **무엇을 못 하는지**를 먼저 밝힙니다. 수치를 인용하기 전에 반드시 함께 읽으세요.

**취급 범위**

- **서울 · 상업업무용 · 매매뿐입니다 (v1).** 아파트 · 오피스텔 · 단독다가구 · 토지 · 전월세,
  그리고 서울 외 지역은 조회되지 않습니다.
- **통건물(일반)만 다룹니다 — 집합(구분상가) 거래는 모든 응답에서 제외됩니다.** 여기서 나오는
  시세 · 평단가는 전부 통건물 기준이며, **구분상가 한 칸 시세로 인용하면 안 됩니다.** 제외된
  집합 거래 건수는 응답의 `jiphap_excluded` 로 함께 알려줍니다.

**매칭의 한계**

- **매칭은 확률이지 등기부가 아닙니다.** `추정매칭` 은 동일 스펙 인접 건물일 수 있고,
  `인접후보` 는 확인 없이 인용하면 안 됩니다. `추정매칭` 이하는 `match_explain` 으로 근거를
  확인한 뒤 쓰세요.
- **특정되지 않는 거래가 있습니다.** 표제부와 일치하는 필지를 못 찾으면 `마스킹 미복원` 으로
  표기됩니다 — 그 건의 **금액 · 면적은 실측값이지만 주소는 확정된 것이 아닙니다.**
- **왜 100% 매칭은 불가능한가 (면적 오차의 원인).** 매칭은 표제부의 **연면적 · 대지면적 ·
  건축년도(사용승인일)** 를 실거래 신고행과 대조하는데, 이 값들은 서로 다른 공부(건축물대장 vs
  토지대장)에서 나오기 때문에 완전히 일치하지 않을 수 있습니다.
  - **대지면적이 공부마다 다릅니다.** 코너 필지의 **가각전제**, **도로 확폭 · 건축선 후퇴**가
    있으면 실제 건축에 쓸 수 있는 땅이 줄어 **토지대장 대지면적**과 **건축물대장 대지면적**이
    어긋납니다. 또 필지가 여러 지번으로 **합필**된 경우 주지번 하나만으로는 면적이 맞지 않아 —
    이 도구는 **부속지번(보조지번) 대장을 합산해 대표지번으로 재앵커**하고, 다필지로 확인되면
    대지면적 허용 오차를 완화해 보정합니다.
  - **연면적도 공부상 차이가 있습니다.** **옥탑** 등 일부 공간이 대장에 반영되지 않는 경우가
    있어 신고 연면적과 표제부 연면적이 딱 떨어지지 않을 수 있습니다.

  그래서 이 도구는 **면적 오차를 단계적으로 허용하는 매칭 파이프라인**을 씁니다:

  0. **마스킹 자릿수 프리필터** — 마스킹 `2**`는 "본번이 정확히 3자리이고 2로 시작"을
     뜻합니다(실측 규칙). 자릿수까지 검사해 후보 풀을 좁혀 오매칭 여지를 줄입니다.
  1. **정확매칭** — 연면적 · 대지면적 · 건축년도 3속성 완전일치(사실상 확정)
  2. **건축년도 + 연면적** 일치
  3. **오차범위 ±10%**(연면적 · 대지) — 합필 필지는 대지 검사 완화
  4. **지분거래 비율매칭** — 지분 매매는 신고 면적이 건물 전체가 아니라 **지분 몫**이라
     절대값 비교가 원리상 불가능합니다. 대신 **연면적 지분율 ≈ 대지면적 지분율**(등기
     지분율은 두 면적에 동일하게 적용됨)이 0.2% 이내로 일치하는 후보가 **유일**할 때만
     복원합니다. 지분거래가 작은 건물의 전체 스펙과 우연히 겹쳐 생기던 오매칭도 함께
     차단됩니다.
  5. **추정매칭(전부 유일후보 한정)** — 건축년도 결측 시 연면적(+대지) 정확일치,
     건축년도 ±1년(연말 준공 · 이월 등기 노이즈) + 연면적 정확일치, 대지 공부 오차 시
     연면적±10% + 건축년도 일치 등 — 어느 경우든 **후보가 유일**할 때만 복원.
     **후보가 둘 이상이면 확정하지 않습니다(옆 건물 오매칭 방지).**

  위에서 아래로 갈수록 신뢰도를 낮춰 `정확매칭` · `추정매칭` · `인접후보`로 표기합니다.

  **매칭 정확도는 표제부 데이터 완전성에 좌우됩니다** — 건축HUB API의 일시 오류로 표제부가
  부분 수신되면 그 동의 복원율이 떨어집니다. 이 도구는 **5xx 재시도 + 부분수신본 캐시 방지 +
  JSON/XML 겸용 파싱**(건축HUB가 2026-08부터 기본 응답을 JSON으로 변경)으로 항상 완전한
  표제부로만 매칭합니다. 대형 동도 절단 없이 수신합니다(신림동 17,795건 실측 대응).

  (측정: **서울 25개 구 전수** — 거래 발생 352개 동, 12개월, 통건물 마스킹 매매 2,547건 —
  복원율 **84.3%**(해제 제외 시 84.7%), 확정급 1 · 2단계 68.1%, 합성 자가검증 오매칭 0 —
  기준 2026-08 · 표본 기간에 따라 달라짐)

**데이터 · 통계**

- **해제신고(계약 취소) 건은 집계에서 제외**하고 그 건수를 알려줍니다. 취소된 값을 실거래로
  오인하지 않도록 한 조치입니다.
- **`area_scan` 통계는 표본이 얇을 수 있습니다.** 통건물 매매는 동에 따라 월 1~7건이라, 반드시
  `coverage`(모수 분해: 전체 · 집합 제외 · 해제 제외 · 마스킹 미복원)를 함께 보고 대표성을
  판단하세요.
- **첫 조회는 느리고, 캐시는 최신이 아닐 수 있습니다.** 구·동 첫 조회는 건축물대장 전체를 받느라
  수십 초~수 분 걸립니다(이후 15분 캐시). 실거래는 매달 갱신되므로 오래된 캐시·프리워밍 데이터는
  최신 거래를 반영하지 못할 수 있습니다.

**원칙**

- **결과가 없으면 `[NOT_FOUND]` 를 반환합니다 — 데이터가 없는 것이지 0원이 아닙니다.** 이 경우
  수치를 지어내면 안 됩니다(AI가 환각하지 못하도록 설계된 신호입니다).
- **소유자 등 개인정보는 다루지 않습니다.** 공개된 실거래 · 건축물대장 실측값만 반환합니다.

> 위 한계를 넘는 판단(계약 · 감정 · 고객 제시)에는 반드시 원문을 직접 확인하세요.
> 아래 고지를 함께 읽어주세요.

---

## 고지

이 도구의 결과는 **참고자료이며 중개대상물 확인·설명서가 아닙니다.**
공적장부의 원문(국토교통부 실거래가 공개시스템, 건축물대장)이 언제나 우선합니다.
고객에게 제시하거나 계약 판단에 쓰기 전에 원문을 직접 확인하세요.
매칭 결과의 정확성에 대해 제작자는 책임지지 않습니다.

출처: 국토교통부 실거래가 공개시스템 · 건축HUB (공공데이터포털 data.go.kr)

---

## Star History

[![Star History Chart](https://api.star-history.com/svg?repos=syleedlabs/deal-locator-mcp&type=Date)](https://star-history.com/#syleedlabs/deal-locator-mcp&Date)

---

## 라이선스

MIT License — Copyright (c) 2026 디랩스(DLABS)

동봉 폰트 [Pretendard](https://github.com/orioncactus/pretendard) 는 SIL Open Font
License 1.1 입니다. 폰트에는 MIT 가 적용되지 않습니다 — [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md) 참조.

## 문의

디랩스(DLABS) · [github.com/syleedlabs](https://github.com/syleedlabs)

TDQS

A4/5.0

Scored across 6 tools

Disambiguation5/5

Each tool has a distinct and well-defined purpose. resolve_address handles address parsing, deal_card_search retrieves the latest deal card for a specific parcel, deal_history provides full transaction history, match_explain details matching logic, area_scan offers area-level statistics, and deal_card_create generates a PNG card. There's no functional overlap.

Naming Consistency5/5

All tool names follow a consistent verb_noun snake_case pattern (e.g., resolve_address, deal_card_search, area_scan). The naming is predictable and clearly describes each tool's action.

Tool Count5/5

With 6 tools, the server covers the core workflows of a real estate deal locator: address resolution, individual deal lookup, history, matching explanation, area scanning, and card generation. The count feels appropriate for the domain and avoids bloat.

Completeness4/5

The tool set covers essential operations for locating commercial property deals in Seoul, including address lookup, detailed parcel info, history, matching explanation, and area-wide scanning. A minor gap is the lack of a direct search by specific building name or address beyond the initial resolve, but area_scan with road filter partially addresses this.

Maintenance

ActivityActive
ResponsivenessNo issues