naver-reservation-watcher-mcp
# naver-reservation-watcher-mcp
네이버 예약(식당/카페/호텔 등)의 빈자리를 백그라운드에서 계속 감시하고, 사용자가 지정한
조건이 충족되면 **자동으로 예약**하거나 **알려주고 승인 후 예약**하거나 **알림만** 보내는
시스템입니다. Claude Desktop이 꺼져 있어도 감시는 계속됩니다.
**이 프로젝트는 각자 자신의 PC에 설치해서 자신의 네이버 계정으로 직접 로그인해 쓰는
용도입니다.** 어떤 서버도 사용자의 로그인 정보나 예약 데이터를 수집하지 않습니다 - 코드를
내려받아 실행하는 이 PC 안에만 모든 데이터(로그인 세션, 감시 목록, 예약 기록)가 남습니다.
## 구조
```
Claude Desktop <--(MCP, stdio)--> nrw.mcp_server.server ┐
├── 같은 SQLite DB (data/nrw.sqlite3) 공유
Windows 작업 스케줄러 --(상시 실행)--> nrw.watcher.service ┘
│
└── Playwright persistent profile (data/browser_profile) 로
네이버 로그인 세션을 재사용해 실제 감시/예약 수행
```
- **MCP 서버**는 브라우저를 직접 조작하지 않습니다. `check_availability`/`reserve_now`/
`approve_reservation` 은 SQLite `jobs` 테이블에 작업을 넣고, watcher가 처리한 결과를
기다려서 돌려주는 얇은 클라이언트입니다. (Playwright persistent profile은 한 번에 하나의
프로세스만 열 수 있기 때문입니다.)
- **watcher 서비스**가 실제로 Playwright로 브라우저를 열어 감시/예약을 수행하는 유일한
프로세스입니다. Claude Desktop과 별개로, Windows 작업 스케줄러에 등록되어 로그온 시
자동으로 계속 실행됩니다.
- 감시 내역, 예약 결과, 작업 큐, 이벤트 로그는 모두 `data/nrw.sqlite3` (SQLite, WAL 모드)에
저장됩니다. 이 `data/` 폴더는 설치 후 각 사용자의 PC에서 자동으로 새로 생성되며,
**저장소에 포함되어 있지도, 커밋되지도, 어디로도 전송되지도 않습니다.**
## 설치
```powershell
git clone https://github.com/LEESUJEONG1030/naver-reservation-watcher-mcp.git
cd naver-reservation-watcher-mcp
powershell -ExecutionPolicy Bypass -File scripts\setup_venv.ps1
```
가상환경 생성, 의존성 설치, Playwright Chromium 다운로드까지 한 번에 진행합니다. 이후
`data/` 디렉터리(SQLite DB, 로그, 브라우저 프로필)는 이 프로젝트 폴더 안에 자동으로
생성됩니다 - 설정 없이 그대로 써도 되고, `NRW_DATA_DIR` 환경변수로 다른 위치를 지정할
수도 있습니다.
> Windows 전용입니다 (Task Scheduler, `win11toast` 알림에 의존). Playwright/Python 부분
> 자체는 크로스플랫폼이지만, 지금은 Windows에서의 상시 실행 경로만 만들어졌습니다.
## 1) 네이버 로그인 (최초 1회, 반드시 직접 해야 하는 단계)
비밀번호는 코드/설정 어디에도 저장하지 않습니다. Playwright persistent profile을 이용해
로그인 세션(쿠키)만 이 PC의 `data/browser_profile` 에 재사용합니다 - 이 폴더는 다른 곳으로
복사/전송/커밋되지 않아야 합니다(`.gitignore`에 이미 포함되어 있습니다).
```powershell
.venv\Scripts\python.exe scripts\login_naver.py
```
열리는 브라우저 창에서 직접 로그인(2단계 인증 포함)하면, 로그인 완료를 자동으로 감지해
세션을 저장합니다. watcher가 실행 중이어도 실행할 수 있습니다.
## 2) watcher 서비스 실행
수동으로 (디버깅용, 창을 띄워 로그를 바로 보고 싶을 때):
```powershell
powershell -ExecutionPolicy Bypass -File scripts\run_watcher.ps1
```
**재부팅해도 자동으로 계속 실행**되게 하려면 Windows 작업 스케줄러에 등록:
```powershell
powershell -ExecutionPolicy Bypass -File scripts\register_task_scheduler.ps1
```
- 이 사용자로 로그온할 때 자동 시작, 실패 시 최대 999회까지 1분 간격으로 재시작하도록
등록됩니다. 시스템에 영구적으로 남는 작업이므로 필요 없어지면
`scripts\unregister_task_scheduler.ps1` 로 제거하세요.
- 로그는 `data\logs\watcher.log` 에 남습니다.
## 3) Claude Desktop에 MCP 서버 연결
Claude Desktop 버전/설치 방식에 따라 두 가지 방법 중 하나를 씁니다. **Extensions 설치
UI가 있으면 방법 A**를, 그 메뉴가 없거나 동작하지 않으면 **방법 B**를 쓰세요 - 실제로
Microsoft Store(MSIX)로 설치된 Claude Desktop 일부 버전에는 방법 A의 메뉴 자체가 없는
경우가 있었고, 그때는 방법 B로 정상적으로 연결됐습니다.
### 방법 A: `.mcpb` Desktop Extension 설치 (Extensions 설치 UI가 있는 경우)
1. `mcpb/` 폴더가 `manifest.json` + `server/main.py`(얇은 launcher)로 구성되어 있습니다.
패키징하려면:
```powershell
npx --yes @anthropic-ai/mcpb validate mcpb\manifest.json
npx --yes @anthropic-ai/mcpb pack mcpb dist\naver-reservation-watcher-mcp.mcpb
```
2. Claude Desktop에서 **Settings → Extensions → Advanced settings → Install Extension…**
로 방금 만든 `.mcpb` 파일을 설치합니다.
3. 설치 시 "프로젝트 설치 경로" 설정에 **이 프로젝트를 클론/설치한 실제 폴더**를 직접
지정합니다. 이 확장은 그 경로의 `.venv\Scripts\python.exe` 로 실행되고, 같은 경로의
`data\nrw.sqlite3` 를 watcher와 공유합니다 - 확장 자체는 Playwright나 watcher를 직접
실행하지 않고, 이미 실행 중인 watcher와 SQLite `jobs`/`watches` 테이블을 통해서만
통신하는 얇은 MCP 인터페이스입니다.
4. 일반 채팅에서 `add_reservation_watch` 등 7개 도구가 보이는지 확인합니다.
### 방법 B: 로컬 MCP 서버 직접 등록 (Extensions 설치 UI가 없는 경우)
일부 Claude Desktop 설치(특히 Microsoft Store/MSIX 버전)는 **Settings → Extensions**
메뉴 자체가 없을 수 있습니다. 이 경우 **Settings → Developer(개발자) → Local MCP
servers(로컬 MCP 서버) → Edit Config(구성 편집)** 로 들어가면 열리는 설정 파일에
`mcpServers` 항목을 직접 추가합니다.
`<프로젝트경로>` 는 이 프로젝트를 클론/설치한 **본인의 실제 폴더 경로**로 바꿔주세요
(예: `C:\Users\you\naver-reservation-watcher-mcp-oss`). 아래는 형태를 보여주는
예시이며, 실제 값은 각자 환경에 맞게 채워야 합니다:
```json
"mcpServers": {
"naver-reservation-watcher-mcp": {
"command": "<프로젝트경로>\\.venv\\Scripts\\python.exe",
"args": ["-m", "nrw.mcp_server.server"],
"cwd": "<프로젝트경로>"
}
}
```
기존에 다른 MCP 서버 항목이 이미 있다면 `mcpServers` 객체 안에 이 항목만 추가하고
나머지는 그대로 두세요. 저장 후 Claude Desktop을 재시작하면 방법 A와 동일하게
`add_reservation_watch` 등 7개 도구가 보여야 합니다.
두 방법 모두 이 MCP 서버는 Playwright나 watcher를 직접 실행하지 않습니다 - 이미
실행 중인 watcher와 SQLite `jobs`/`watches` 테이블을 통해서만 통신하는 얇은
인터페이스입니다. (1)/(2) 단계를 먼저 완료해 watcher가 실행 중이어야 합니다.
## 사용 예시 (Claude 채팅에서)
- "OO식당 10월 3일 저녁 6시~8시, 2명, 19시 우선으로 감시해줘. 자리 나면 자동으로 예약해줘."
→ `add_reservation_watch(store_name="OO식당", target_date="2026-10-03", time_min="18:00", time_max="20:00", party_size=2, time_priority=["19:00"], mode="AUTO")`
- "지금 감시 목록 보여줘" → `list_reservation_watches()`
- "지금 예약 가능한 시간 있어?" → `check_availability(watch_id=...)`
- "지금 바로 19시로 예약해줘" → `reserve_now(watch_id=..., time="19:00")`
- "실제로 예약하기 전에 흐름만 점검해줘 (클릭은 하지 마)" → `reserve_now(watch_id=..., time="19:00", dry_run=True)`
- "그 예약 진행해줘" (ASK 알림을 받은 뒤) → `approve_reservation(watch_id=..., approve=True)`
- "예약 상태 어때?" → `reservation_status(watch_id=...)`
## 예약 방식 (mode)
- `AUTO`: 조건(시간대/우선순위)에 정확히 맞는 자리가 나오면 자동으로 예약을 진행합니다.
단, **결제/선결제/보증금/취소수수료/마케팅성 동의가 필요하면 절대 자동으로 진행하지
않고** `AWAITING_APPROVAL` 상태로 전환해 사용자에게 알립니다. 예약 완료에 필수인 일반
이용약관/개인정보 동의(화면에 "필수"로 명확히 표시된 것만)는 자동으로 체크합니다 -
아래 "필수 동의 자동 처리" 참고.
- `ASK`: 자리를 발견하면 "OO식당 10/3 19:00 2명 자리가 생겼습니다. 예약할까요?" 알림을
Windows 토스트로 보냅니다. 토스트에는 **예약하기 / 이번 자리 넘기기** 버튼이 있어
Claude Desktop을 열지 않아도 바로 승인/거절할 수 있습니다. 채팅에서
`approve_reservation` 으로도 동일하게 처리할 수 있습니다. **승인 즉시 클릭하지
않습니다** - 예약을 시도하기 직전에 그 슬롯이 여전히 유효한지 다시 조회하고, 이미
사라졌다면 예약을 진행하지 않고 `WATCHING` 상태로 돌아가 사용자에게 알립니다.
- `NOTIFY`: 알림만 보내고 예약은 하지 않습니다.
**중복 알림 방지**: 같은 시간대가 계속 열려 있는 동안은 같은 알림을 반복해서 보내지
않습니다. 다만 그 시간이 마감되었다가 취소 등으로 다시 열리면(재오픈), 새로운 발견으로
간주해 다시 알릴 수 있습니다.
## 안전장치
- CAPTCHA, 추가 본인인증, 비밀번호 재입력이 감지되면 절대 우회하지 않고 `NEEDS_HUMAN`
상태로 전환해 사용자에게 알리고 대기합니다. `NEEDS_HUMAN` 상태인 동안은 자동 감시를
완전히 일시 중지합니다 - 사용자가 직접 확인을 완료한 뒤 Claude에게 "다시 확인해줘"
(=`check_availability(watch_id=...)`)라고 요청하면 감시가 재개됩니다.
- 결제/선결제/보증금/취소수수료/마케팅성 동의 화면이 보이면 `AWAITING_APPROVAL` 로
전환하고 절대 자동으로 결제/동의하지 않습니다.
- **필수 동의 자동 처리 (AUTO_ACCEPT_REQUIRED_TERMS)**: 예약 완료에 반드시 필요한, 화면에
"필수"로 명확히 표시된 이용약관/개인정보 동의 체크박스만 자동으로 체크합니다.
마케팅 수신/알림받기/광고성/멤버십 가입 등은 필수로 표시돼 있어도 절대 자동 체크하지
않습니다. 필수/선택 여부를 확실히 판별할 수 없는(새로운/다른 형태의) 동의 항목을
만나면 추측하지 않고 `NEEDS_HUMAN` 으로 멈춥니다. 자동으로 체크한 항목 이름은 이벤트
로그와 알림에 남습니다.
- 실제 예약 버튼을 누르기 직전, 화면에 표시된 날짜/시간/인원을 다시 읽어 요청과 다르면
중단합니다 (`reserve_now`, ASK 승인, AUTO 모두 동일하게 적용).
- 예약 성공 시 업체명/날짜/시간/인원/예약번호(확인 가능한 경우)/적용한 쿠폰/자동 체크한
필수 동의 항목을 이벤트 로그와 Windows 토스트에 남기고 해당 감시를 자동 종료합니다.
실패해도 원인을 명확히 남기고, 재시도 가능한 실패(검증 실패/슬롯 선점 등)는 감시를
계속합니다.
- `reserve_now(..., dry_run=True)`: 실제 예약과 완전히 동일한 절차(날짜/인원/시간 선택,
필수 동의 자동 체크, 쿠폰 확인, 안전 게이트 확인, 클릭 직전 재검증)를 그대로 수행하되
**최종 확인 버튼은 절대 클릭하지 않고** 어디까지 도달했는지, 어떤 버튼을 누르게
됐을지를 보고합니다. 사이트가 바뀐 뒤에도 안전하게 흐름을 점검할 수 있는 도구입니다.
## 쿠폰 자동 수집/적용
`nrw/coupon_utils.py` 가 선택 로직을, 각 어댑터가 조회/다운로드/적용을 담당합니다. 쿠폰은
두 곳에서 확인합니다: 업체 홈 페이지(`/home`)의 쿠폰 목록, 그리고 예약 화면 자체에 내장된
쿠폰 선택 UI(있는 경우) - 후자가 실제 이 예약에 적용 가능한지에 대해 더 신뢰도 높은
소스입니다.
- **무료로 즉시 받을 수 있는/선택 가능한 쿠폰은 자동으로 받아 적용**합니다 (ASK/AUTO 모드
모두). 이미 보유 중인 쿠폰은 다시 받지 않습니다.
- 여러 쿠폰을 받을 수 있으면 **그 예약의 예상 결제금액 기준으로 실제 할인액이 가장 큰
쿠폰**을 고릅니다.
- **유료 멤버십 가입/구독/개인정보 추가 제공/마케팅 동의/별도 결제가 필요한 쿠폰은
자동으로 받거나 적용하지 않습니다** - 존재는 알림/이벤트 로그에 남기되("확인 필요"),
항상 사용자 확인을 거쳐야 합니다.
- **예약 버튼을 누르기 직전, 선택했던 쿠폰이 여전히 유효한지 다시 확인**합니다. 그
사이 만료되거나 더 이상 적용할 수 없게 됐다면 할인 없이 강행하지 않고 멈춰서 알립니다.
- 예약 성공 기록(`reservation_status`)에 적용한 쿠폰명과 할인액이 함께 저장됩니다.
- 실제 네이버 업체 페이지 두 곳(서로 다른 업체)을 대상으로 쿠폰 조회/게이트 감지 및
`dry_run` 전체 흐름(최종 확인 버튼 직전까지)을 검증했습니다. 실제 예약 확정 클릭은
검증하지 않았습니다 - 업체별 DOM 구조 차이가 있을 수 있어 셀렉터는
`naver_selectors.py` 의 `COUPON_*`/`SUBMIT_BUTTON_*` 항목에 모여 있습니다.
## 폴링 정책 (네이버 서버에 과도한 요청 방지)
`config/config.toml` (없으면 `config/config.example.toml` 기본값 사용)에서 조정 가능:
- 평상시 45~120초 사이 랜덤 간격으로 확인.
- 에러가 나면 30초부터 지수 백오프(최대 15분).
- 페이지에 "O월 O일 O시부터 예약 가능" 같은 오픈 예정 문구가 보이면, 그 시각 2분 전부터
5~15초 간격으로 타이트하게 전환해 오픈 직후를 빠르게 잡습니다.
- 마감된 시간이 취소 등으로 다시 열리는 것도 매 확인마다 이전 스냅샷과 비교해 감지합니다.
## 알림 (Notifier)
기본은 Windows Toast (`win11toast`). `nrw/notifier/base.py` 의 `Notifier` 인터페이스만
구현하면 다른 채널(Telegram 등)로 쉽게 바꿀 수 있습니다 - 뼈대는
`nrw/notifier/telegram_stub.py` 에 있습니다. 채널 전환은 `config.toml` 의
`[notify] channel = "telegram"` 로 지정합니다.
## 테스트 (실제 네이버를 건드리지 않는 mock/로컬 픽스처)
```powershell
.venv\Scripts\python.exe -m pytest -q
```
`mock_site/server.py` 가 로컬에 "오픈 전 → 오픈 → 마감 → 취소자리 발생 → 예약 성공"
전체 흐름을 흉내내는 페이지를 띄우고, `tests/test_e2e_mock_flow.py` 가 watcher의 핵심
로직(오픈 감지, 우선순위 매칭, AUTO/ASK/NOTIFY 분기, 결제/캡차 안전장치, 클릭 직전
재검증)을 실제 Playwright로 그 페이지를 조작하며 검증합니다.
쿠폰 시나리오, dry-run 흐름, 필수 동의 자동 처리 정책, 실제 예약 위젯의 DOM 구조(로컬
정적 HTML 픽스처로 재현)는 각각 `tests/test_coupons_e2e.py`, `tests/test_dry_run.py`,
`tests/test_naver_widget_flow.py`, `tests/test_reservation_logging.py` 에서 검증합니다.
## 한계 / 고지
- **네이버 예약 페이지의 실제 DOM 구조는 업체/업종마다 다르고 네이버가 자주 개편합니다.**
`src/nrw/browser/naver_selectors.py` 에 문구/셀렉터를 한곳에 모아뒀으니, 특정 업체에서
동작이 어긋나면 이 파일만 조정하면 됩니다.
- 실제 예약 확정(최종 버튼 클릭)은 검증하지 않았습니다 - 로그인은 사용자 본인만 할 수
있고, 실제 결제/예약을 만드는 행위이기 때문입니다. `reserve_now(dry_run=True)` 로
최종 클릭 직전까지의 전체 흐름을 안전하게 점검할 수 있습니다.
- headless 여부는 `config.toml` 의 `[browser] headless` 로 조정합니다. 기본은 `false`
(창이 보이는 채로 실행) 이며, 결제/캡차 등 사용자 확인이 필요할 때 화면을 볼 수 있게
하기 위함입니다.
- watcher는 PC가 켜져 있고 로그온되어 있어야 동작합니다 (완전 종료/절전 상태에서는 동작
하지 않습니다). 이 프로젝트는 로컬 실행 구조라 "PC가 꺼져 있어도 클라우드에서 계속
감시"는 지원하지 않습니다.
## SQLite 스키마 요약
- `watches`: 감시 등록 정보 + 현재 상태
- `reservations`: 성공한 예약 기록
- `jobs`: MCP → watcher 작업 큐 (즉시 확인/예약/승인/거절)
- `events`: 감시별 이벤트/알림 로그
- `heartbeat`: watcher 생존 확인용
## 라이선스
[MIT](LICENSE)
TDQS
Scored across 7 tools
Each tool targets a distinct operation: watch CRUD, availability checking, reservation execution, approval, and status. Even though add_reservation_watch, check_availability, and reserve_now can share store/date/party parameters, their descriptions clearly distinguish monitoring, checking, and immediate action.
Most tools follow a snake_case verb-first pattern (list_, remove_, add_, check_, approve_, reserve_). reservation_status breaks the pattern by being noun-first, and list_reservation_watches is plural while remove/add_reservation_watch are singular, but the overall style is still predictable.
Seven tools is appropriate for this server's scope: watch lifecycle (list/add/remove), availability check, immediate reservation, approval, and status. Each tool covers a necessary step without redundancy.
The tool surface covers watch management, availability checking, immediate reservation, approval/rejection, and status. Minor gaps exist—no update_reservation_watch and no explicit cancel_reservation—but these are workable for the core watcher workflow.