Skip to main content
Glama
rajephon
by rajephon
README.md
# k-lottery

![Python 3.13](https://img.shields.io/badge/python-3.13-blue)
![uv](https://img.shields.io/badge/managed%20by-uv-de5fe9)
![MCP](https://img.shields.io/badge/MCP-FastMCP-000)
![License](https://img.shields.io/badge/license-MIT-green)

동행복권 로또 6/45를 대신 사고, 잔액도 보고, 당첨됐는지도 확인해주는 도구예요.
터미널에서 명령어로 써도 되고, Claude Code나 Claude Desktop에서 말로 시켜도 돼요.
CLI 4개랑 MCP 서버가 같은 코어를 써요.

> 직접 쓰려고 만든 개인용 도구예요. **진짜 돈이 나가니까** 쓰기 전에
> [면책](#면책)을 꼭 읽어주세요.

```
$ uv run scripts/balance.py
구매가능금액        12,000 원
총 예치금           12,000 원
마일리지               500 원

오늘 구매액              0 / 150,000 원
이번주 구매액        5,000 원
최근 1달            21,000 원

고정 가상계좌  7010XXXXXXXXXX
```

## 목차

- [무엇을 할 수 있나](#무엇을-할-수-있나)
- [요구사항](#요구사항)
- [빠른 시작](#빠른-시작)
- [MCP 서버 설정](#mcp-서버-설정)
- [안전장치](#안전장치-돈이-움직이는-경로)
- [CLI 사용법](#cli-사용법)
- [환경변수](#환경변수)
- [종료 코드](#종료-코드)
- [자동화 예시 (cron)](#자동화-예시-cron)
- [알아둘 점](#알아둘-점)
- [개발](#개발)
- [기여](#기여) · [면책](#면책) · [라이선스](#라이선스)

## 무엇을 할 수 있나

| CLI | 하는 일 |
| --- | --- |
| `scripts/balance.py` | 예치금·구매 한도·서약 유효성 |
| `scripts/check.py` | 구매 내역과 당첨 결과 (등수는 서버가 계산) |
| `scripts/deposit.py` | 충전용 가상계좌 발급 (이체는 직접) |
| `scripts/buy.py` | 자동번호 구매 (기본 dry-run) |

| MCP 도구 | 파라미터 | 노출 | 하는 일 |
| --- | --- | --- | --- |
| `lotto_status` | — | 항상 | 잔액·한도·서약 유효성·가상계좌(**뒤 4자리만**) |
| `lotto_results` | `days=14`(최대 91), `round_no?` | 항상 | 구매 내역 + 당첨 결과 |
| `lotto_draw` | `round_no` | 항상 | 공개 추첨 결과 (로그인 불필요) |
| `lotto_buy_precheck` | `games=5` | 항상 | 구매 전 점검 + 회차에 남은 게임 수. **절대 사지 않음** |
| `lotto_buy` | `games=5` | 게이트 | 실제 구매 (승인 필수) |
| `lotto_deposit` | `amount` | 게이트 | 가상계좌 발급 (승인 필수) |

## 요구사항

| | |
| --- | --- |
| [uv](https://docs.astral.sh/uv/getting-started/installation/) | 필수예요. Python 3.13은 uv가 `.python-version`을 보고 알아서 받아와요 |
| 동행복권 계정 | 아이디/비밀번호로 로그인하는 계정이요 |
| 건전구매 서약 | 사이트에서 **직접** 하셔야 해요. 만료됐으면 구매가 막혀요 (종료코드 3) |
| 예치금 | 충전하려면 은행 이체가 필요해서 완전 무인화는 안 돼요 |
| OS | macOS에서 확인했어요. Linux도 될 것 같고, Windows는 안 해봤어요 |

## 빠른 시작

```bash
git clone https://github.com/rajephon/k-lottery.git
cd k-lottery
uv sync

cp .env.example .env    # 아이디·비밀번호 채우기 (.env는 커밋되지 않아요)
uv run scripts/balance.py
```

## MCP 서버 설정

CLI와 같은 코어 위에 FastMCP(stdio) 서버를 올려뒀어요. 잔액이나 당첨 결과를 대화로 물어볼 수
있고, 원하면 구매·충전까지 열 수 있어요. 다만 실제로 사는 건 **매번 직접 승인**하셔야 해요.

### Claude Code에 등록

```bash
# 조회 전용 (기본). 이 저장소 디렉터리에서만 보여요
claude mcp add klottery -- uv run --directory /절대경로/k-lottery scripts/mcp_server.py

# 어느 디렉터리에서든 쓰려면 user 스코프로
claude mcp add klottery -s user -- uv run --directory /절대경로/k-lottery scripts/mcp_server.py

# 구매·충전까지 열려면 게이트를 환경변수로 넘겨요
claude mcp add klottery -s user \
    -e KLOTTERY_MCP_ALLOW_BUY=1 -e KLOTTERY_MCP_ALLOW_DEPOSIT=1 -- \
    uv run --directory /절대경로/k-lottery scripts/mcp_server.py
```

> **게이트는 `.env`에 적어도 안 열려요.** `-e` 옵션이나 Desktop 설정의 `env` 블록처럼
> **서버를 띄우는 프로세스 환경**에 넣어야 해요. `.env`는 모델이 고칠 수 있는 파일이거든요.
> 거기서 스위치를 읽으면 "모델이 못 건드리는 곳에 스위치를 둔다"는 전제가 무너져요.

게이트는 **서버가 뜰 때 한 번** 읽고 끝이에요. 나중에 켜거나 끄려면 등록을 지우고 다시 만든 뒤
클라이언트를 재시작하세요 (`claude mcp remove klottery`).

### Claude Desktop에 등록

설정 파일을 열어요. 없으면 새로 만들면 돼요.

- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`

```json
{
  "mcpServers": {
    "klottery": {
      "command": "/opt/homebrew/bin/uv",
      "args": ["run", "--directory", "/절대경로/k-lottery", "scripts/mcp_server.py"],
      "env": { "KLOTTERY_MCP_ALLOW_BUY": "1" }
    }
  }
}
```

- `command`에는 `which uv`로 나온 **절대경로**를 적어요. GUI 앱은 셸 PATH를 물려받지 않거든요.
- `--directory` 뒤도 절대경로여야 해요.
- `env`를 빼면 조회 도구 4개만 떠요.
- 저장했으면 Claude Desktop을 **완전히 껐다가** 다시 켜주세요.

> Claude Desktop이 승인 프롬프트(elicitation)를 지원하는지는 2026년 7월 현재 확인하지
> 못했어요. 지원하지 않으면 조회 도구만 동작하고 구매·충전은 `[APPROVAL_UNSUPPORTED]`로
> 실패해요. 그럴 땐 CLI를 쓰시면 돼요.

### 잘 붙었는지 확인

```bash
claude mcp list     # klottery가 ✔ Connected 인지 봐요
```

Claude Code에서 `/mcp`를 치면 어떤 도구가 올라왔는지 보여요. 조회 4개만 있으면 조회 전용이고,
`lotto_buy`까지 보이면 게이트가 열린 거예요. 서버 로그(stderr)에도 남아요.

```
돈 도구 노출: buy=True deposit=False
```

### 구매 승인 화면

모델이 `lotto_buy`를 부르면 **클라이언트가 사람에게** 이 화면을 띄워요. 금액을 직접 쳐야
넘어가요. 모델은 이 답변 경로에 끼어들 수 없어요.

```
로또 6/45 구매 승인 요청
  대상 회차     1236회 (추첨 2026/08/08)
  게임 수       5게임 (자동)
  결제 금액     5,000원
  회차 누적     이미 0게임 → 구매 후 5게임 / 회차 한도 5게임
  구매가능금액  12,000원 → 구매 후 7,000원
  오늘 누적     0원 / 한도 150,000원
승인하려면 결제 금액(원)을 숫자로 입력하세요.

※ 이 요청은 AI 모델이 호출한 것입니다. 방금 직접 지시하지 않았다면 거절하세요.
```

### 문제 해결

| 증상 | 원인과 해결 |
| --- | --- |
| Claude Code에 klottery가 안 보여요 | 기본 스코프가 `local`이라 그래요. 다른 디렉터리에서도 쓰려면 `-s user`로 다시 등록하세요 |
| `lotto_buy`가 목록에 없어요 | 게이트를 `.env`에 넣으셨을 거예요. `-e`로 옮기고 다시 등록한 뒤 재시작하세요 |
| Desktop에서 서버가 안 떠요 | `command`를 `which uv` 절대경로로 바꿔보세요. GUI 앱은 셸 PATH를 못 봐요 |
| `[APPROVAL_UNSUPPORTED]` | 클라이언트가 승인 프롬프트를 지원하지 않아요. CLI로 처리하세요 |
| `[RECENTLY_DECLINED]` | 직전 승인을 거절했거나 취소했어요. 10분 뒤에 다시 하거나 CLI로 처리하세요 |
| `[CLOSED]` | 판매시간(06:00~24:00, 토요일은 20:00 마감)이 아니에요. 시간 지나면 풀려요 |
| `[CONFIG] 건전구매 서약…` | 사이트에서 직접 재서약하셔야 해요 |
| `[BALANCE]` | 예치금이 모자라요. 가상계좌를 받아서 **직접 이체**해야 충전돼요 |
| `[NETWORK]` | 연결 자체가 안 됐어요. 요청이 나가지 않은 게 확실하니 그대로 다시 시도하시면 돼요 |
| `[RESPONSE_LOST]` | 요청은 나갔는데 답을 못 받았어요. **이미 샀을 수도 있으니** 다시 사기 전에 구매 내역부터 확인하세요 |
| `[ROUND_LIMIT]` | 이 회차에 살 수 있는 한도(5게임)를 넘겼어요. 남은 게임 수는 `lotto_buy_precheck`의 `round_remaining_games`에 있어요 |

## 안전장치 (돈이 움직이는 경로)

**CLI**는 구매·충전이 기본 dry-run이고, `--execute`를 붙여야 실제로 실행돼요. 확인 프롬프트는
stdin이 tty가 아니면 그냥 **거절**해요. cron 같은 데서 얼떨결에 결제되는 걸 막으려고요.

**MCP엔 tty도 `--execute`도 없어서** 다른 방법이 필요했어요. `execute: bool` 같은 파라미터를
두면 모델이 알아서 `true`를 넣어버릴 수 있거든요. 그건 승인이 아니죠. 그래서 층을 나눴어요
(자세한 근거는 [설계 문서](./docs/2026-07-26_klottery-mcp-server.md)에 있어요).

1. **노출** — 구매·충전은 서버를 띄우는 프로세스 환경에 `KLOTTERY_MCP_ALLOW_*`가 있어야만
   목록에 떠요. 모델은 실행 중에도, `.env`를 고쳐서도 이걸 못 바꿔요.
2. **승인** — 실행 직전에 사람이 결제 금액을 직접 입력해야 해요. 프롬프트에는 이 요청을
   모델이 했다는 사실도 같이 띄워요. 프롬프트 주입으로 모델이 멋대로 부른 경우를 구분하시라고요.
3. **막히면 멈춰요** — 승인 프롬프트를 지원하지 않는 클라이언트에서는 그냥 실패해요.
   네트워크 요청은 한 번도 나가지 않아요.
4. **조르기 차단** — 승인 요청은 한 번에 하나만 떠요. 거절·취소·중단하거나 금액을 틀리게
   입력하면 10분 동안 다시 묻지 않아요. 구매와 충전이 이 쿨다운을 같이 써요.
5. **상한** — 한 번에 최대 5게임(5,000원)이고, 나눠 사도 **한 회차 합계 5게임**을 넘길 수
   없어요. 이 한도를 뚫는 `--force`는 MCP에 없어요. 승인을 받은 뒤에도 서버 데이터를 새로
   받아 점검을 다시 돌리니까, 기다리는 사이에 다른 데서 산 게 있으면 그것도 합산돼요.
6. **감사** — 모든 시도가 `data/mcp-audit.jsonl`에 남아요. 상관 ID, 결제 요청을 실제로
   보냈는지, 무슨 오류였는지까지 기록해요. 구매 응답 원문은 성공하든 실패하든 저장하고,
   기록에 실패해도 결제 결과를 뒤집지 않아요.
7. **정보 차단** — 도구 설명이나 반환값에 우회 방법(명령줄, 플래그)을 적지 않아요. 그걸 읽는 건
   사람이 아니라 모델이니까요. 승인에 걸린 시간, 계좌번호 전체, 계좌주 실명, 계정 아이디도
   모델에게 넘기지 않아요.

구매 요청을 보내기 전에 다섯 가지를 확인해요. 하나라도 걸리면 요청 자체를 안 보내요.

1. 게임 수가 1~5인지
2. 건전구매 서약이 살아 있는지
3. **이 회차에 이미 산 게임까지 더해서 5게임을 넘지 않는지** (서버 구매내역 기준이에요.
   CLI의 `--force`로만 넘길 수 있는데, 넘겨봐야 사이트가 거절해요)
4. 일 구매한도를 넘지 않는지
5. 잔액이 충분한지

## CLI 사용법

네 스크립트 모두 `--json`(결과 JSON을 stdout으로), `-v`(DEBUG 로그, stderr),
`--env-file <경로>`를 받아요. 사람이 읽는 로그는 stderr로, 파이프로 넘길 데이터는 stdout으로
나가요.

### 잔액 확인

```bash
uv run scripts/balance.py
uv run scripts/balance.py --json
```

### 당첨 확인

```bash
uv run scripts/check.py            # 최근 14일
uv run scripts/check.py --days 30
uv run scripts/check.py --round 1234
```

조회 기간은 **최대 91일**이에요. 사이트가 3개월까지만 허용하거든요. 그래서 `--round`로 찾을 수
있는 것도 최근 3개월 안에 산 회차까지예요.

등수는 서버가 계산해서 줘요. 내가 산 번호를 따로 저장해둘 필요가 없어요.
사지 않은 회차를 물어보면 공개 추첨 결과를 대신 보여줘요.

### 충전

```bash
uv run scripts/deposit.py --amount 5000            # dry-run
uv run scripts/deposit.py --amount 5000 --execute
```

넣을 수 있는 금액은 `5000` `10000` `20000` `30000` `50000` `100000` `150000`이고,
하루 15만원까지예요.

> **이 명령만으로는 돈이 움직이지 않아요.** 고정 가상계좌에 "얼마 넣을 거다"라고 등록하는
> 게 전부예요. 출력된 계좌로 **직접 이체**해야 예치금이 들어와요.

### 구매

```bash
uv run scripts/buy.py                     # dry-run (기본 5게임)
uv run scripts/buy.py --games 5 --execute
uv run scripts/buy.py --games 4 --execute # 1게임 산 회차에 4게임 더
uv run scripts/buy.py --execute --yes     # cron용
```

자동번호로 사요. **기본은 dry-run**이라 `--execute`를 붙여야 실제로 결제돼요.

한 회차를 **여러 번 나눠 살 수 있어요.** 동행복권이 온라인 구매를 회차당 5,000원까지
허용하거든요. 그래서 이미 1게임 산 회차라면 4게임을 더 살 수 있고, 합계가 5게임을 넘는
요청은 보내기 전에 거절해요(종료코드 8). 판정 기준은 로컬 기록이 아니라 **서버 구매내역**이라
사이트에서 직접 산 것도 같이 계산돼요.

```
$ uv run scripts/buy.py --games 5
대상 회차     1235회 (추첨 2026/08/01)
게임 수       5게임  (자동)
결제 금액     5,000원  (1,000원 x 5)
회차 누적     이미 1게임 → 구매 후 6게임  (회차 한도 5게임)
구매가능금액  4,000원 → 구매 후 -1,000원
RoundLimitError: 1235회는 이미 1게임(1,000원)을 구매했습니다. 회차 한도가 5게임(5,000원)이라
4게임까지만 더 구매할 수 있습니다 (요청 5게임).
```

## 환경변수

계정 정보는 저장소 루트의 `.env`에 넣어요(`cp .env.example .env`). 이 파일은 커밋되지 않아요.

| 환경변수 | 설명 | 기본값 |
| --- | --- | --- |
| `LOTTO_USER_ID` | 동행복권 아이디 | (필수) |
| `LOTTO_PASSWORD` | 동행복권 비밀번호 | (필수) |
| `LOTTO_DATA_DIR` | 쿠키·이력 저장 위치 | `./data` |
| `LOTTO_TIMEOUT` | HTTP 타임아웃(초) | `10` |
| `LOTTO_REQUEST_INTERVAL` | 연속 요청 최소 간격(초) | `0.3` |
| `LOTTO_USER_AGENT` | User-Agent | 크롬 |

MCP 결제 도구를 여는 스위치는 **`.env`가 아니라** 서버를 띄우는 프로세스 환경에서 읽어요.

| 환경변수 | 설명 | 기본값 |
| --- | --- | --- |
| `KLOTTERY_MCP_ALLOW_BUY` | `lotto_buy` 노출 (`1`/`true`/`yes`/`on`) | (안 뜸) |
| `KLOTTERY_MCP_ALLOW_DEPOSIT` | `lotto_deposit` 노출 | (안 뜸) |

> 접두사를 다르게 둔 건 일부러예요. `LOTTO_*`는 `.env`로 들어오는 설정이고,
> `KLOTTERY_MCP_*`는 **`.env`로 들어오면 안 되는** 스위치거든요.

## 종료 코드

cron에서 실패 원인을 구분하시라고 나눠뒀어요.

| 코드 | 의미 | 코드 | 의미 |
| --- | --- | --- | --- |
| 0 | 성공 | 7 | 자동화 차단 (`isAllowed=N`) |
| 1 | 일반 오류 | 8 | 회차 구매한도 초과 |
| 2 | 잘못된 인자·값 | 9 | 판매시간 아님·점검중 |
| 3 | 설정/자격증명 | 10 | 접속 대기열 |
| 4 | 인증 실패 | 11 | 사용자가 확인을 거부 |
| 5 | 서버 응답 이상 | 12 | 연결 실패 (요청 미전달) |
| 6 | 잔액 부족 | 13 | 응답 유실 (요청은 나감) |
| | | 130 | 중단 |

**그대로 재시도해도 되는 건 9·10·12번뿐이에요.** 6번은 충전을, 8번은 게임 수를 줄이지 않으면
몇 번을 돌려도 결과가 같고, 5번(응답 이상)도 마찬가지예요.

12번은 사이트에 아예 못 닿은 거라 요청이 나가지 않은 게 확실해요. 맥이 잠들어 있으면 예약
실행이 이걸로 실패하니, cron에 거실 거면 잠자기를 꺼두시거나 `caffeinate`를 쓰세요.

> **13번은 자동 재시도하면 안 돼요.** 요청은 나갔는데 답을 못 받은 거라 **이미 결제됐을 수도**
> 있어요. 회차 한도가 남아 있으면 사전 점검도 못 막으니(한도 안의 추가 구매는 정상이거든요),
> `scripts/check.py`로 **서버 구매 내역을 먼저 확인**하고 사람이 판단하세요.
>
> 이때는 응답을 못 받았으니 `data/responses/`에 원문도 안 남아요. 남는 건 stderr 로그이고,
> MCP로 산 경우엔 `data/mcp-audit.jsonl`에 `request_sent: true`가 기록돼요 — "요청은 나갔다"
> 까지가 우리가 아는 전부라는 뜻이에요. 그래서 서버 쪽을 봐야 해요.

## 자동화 예시 (cron)

```cron
# uv는 절대경로로 적어요. cron의 PATH는 셸과 다르거든요 (`which uv` 결과를 쓰면 돼요)
# 매주 월요일 19:00 구매, 일요일 09:00 당첨 확인
0 19 * * 1  cd /절대경로/k-lottery && /opt/homebrew/bin/uv run scripts/buy.py --execute --yes >> data/cron.log 2>&1
0  9 * * 0  cd /절대경로/k-lottery && /opt/homebrew/bin/uv run scripts/check.py --days 7 >> data/cron.log 2>&1
```

`buy.py`는 그 회차에 이미 산 게임 수를 서버 내역에서 확인해요. 기본값(5게임)으로 걸어두면
한 번 사고 나서 한도가 차니까 두 번째 실행은 종료코드 8로 멈춰요. 다만 `--games`를 줄여
걸어두면 두 번째 실행이 **남은 한도만큼 더 사요** — 회차당 5,000원까지는 나갈 수 있다는
뜻이니 cron에 거는 값은 그걸 감안해서 정하세요.
판매시간(06:00~24:00, 토요일은 20:00 마감)이 아니면 종료코드 9로 끝나요.

예치금이 떨어지면 종료코드 6으로 실패해요. 충전은 은행 이체가 필요해서 여기까지가 한계예요.

## 알아둘 점

- 사이트가 **2026년에 개편**됐어요. 인터넷에 돌아다니는 기존 봇 코드(`common.do?method=...`,
  `userSsl.do?method=login`)는 전부 죽은 엔드포인트를 써요.
- 구매 요청에 **자동화 탐지**(`isAllowed=="N"`)가 있어요. 아직 걸려본 적은 없지만, 걸리면
  종료코드 7로 확실하게 실패해요.
- 모바일 구매는 토·일에 막혀요. 이 도구는 PC 경로를 써요.
- 판매시간이 아니면 사이트가 HTTP 200으로 멀쩡한 페이지를 주면서 구매 폼만 빼놔요.
  이 경우를 `[CLOSED]`로 따로 구분해요.

## 개발

```bash
uv run pre-commit install   # 훅 설치 (최초 1회)

uv run ruff check --fix .   # 린트
uv run ruff format .        # 포맷
uv run ty check             # 타입 검사
uv run pytest               # 테스트 (네트워크 안 탐)
```

테스트는 실제 네트워크로 나가지 않아요. httpx 호출은 `respx`로 목킹하고, 소켓 가드가 이 규약을
강제해요. 목킹을 빠뜨린 테스트는 통과가 아니라 실패해요.

```
src/klottery/
├── endpoints.py   URL·필드명·상수 — 사이트가 바뀌면 여기만 고친다
├── dhlottery.py   사이트 클라이언트
├── models.py      응답 파싱
├── flows.py       구매·충전·조회 조립 (CLI·MCP 공유)
├── payloads.py    JSON 빌더 (CLI --json과 MCP 출력의 단일 계약)
├── purchase.py    구매 전 점검 (순수 함수)
├── session.py     httpx 세션 + 쿠키 영속화
├── crypto.py      로그인 RSA
├── clock.py       KST 시각·날짜 계산
├── cli.py         공통 인자·종료코드 (CLI 전용)
├── config.py      설정
├── errors.py      도메인 예외
├── log.py         로깅·비밀값 마스킹
├── storage.py     data/ 원자적 쓰기
└── mcp/           MCP 서버 배선 — 도메인 로직은 안 둔다
    ├── server.py    조립 + 노출 게이트
    ├── tools.py     도구 정의
    ├── confirm.py   승인·쿨다운 (유일 접점)
    ├── runtime.py   락·에러 변환·감사 로그
    └── errors.py    도메인 예외 → ToolError 코드 표
scripts/           진입점 5개 (CLI 4개 + mcp_server.py)
tests/             pytest (respx 목킹)
data/              쿠키·이력 (gitignore)
```

- 작업 규약: [CLAUDE.md](./CLAUDE.md)
- 사이트 API 실측 스펙: [docs/dhlottery-api-spec.md](./docs/dhlottery-api-spec.md)
- 설계: [docs/2026-07-26_dhlottery-cli.md](./docs/2026-07-26_dhlottery-cli.md) ·
  [docs/2026-07-26_klottery-mcp-server.md](./docs/2026-07-26_klottery-mcp-server.md)

## 기여

개인용 도구라 기능 요청은 받지 않아요. 사이트가 바뀌어서 깨진 부분이라면 이슈나 PR을 환영해요.
다만 그럴 땐 `docs/dhlottery-api-spec.md`에 실제로 캡처한 요청·응답을 같이 올려주세요.
추측으로 쓴 엔드포인트는 머지하지 않아요.

## 면책

- **본인 계정**에 쓰라고 만든 개인용 도구예요. 남의 계정에 쓰라고 만든 게 아니에요.
- 동행복권 이용약관이나 자동화 정책에 걸리는지는 쓰는 분이 판단하셔야 해요. 구매 응답에
  자동화 탐지(`isAllowed=="N"`)가 있고, **계정이 정지될 수 있어요.**
- **진짜 돈이 나가요.** 잘못 사거나 잘못 충전해서 생긴 손해는 쓰는 분 몫이에요.
- 복권은 사행성 상품이에요. 자동화하면 나도 모르게 사는 횟수가 늘어요. 동행복권 구매한도랑
  이 도구 상한을 같이 걸어두시길 권해요. 혹시 도박 문제로 힘드시다면 국번 없이 **1336**으로
  전화해 보세요.
- 사이트가 개편되면 언제든 안 돌아갈 수 있어요. 아무것도 보증하지 않아요(AS-IS).

## 라이선스

[MIT](./LICENSE)

TDQS

A4.1/5.0

Scored across 4 tools

Disambiguation5/5

Each tool has a clearly distinct purpose: status checks account-level limits, results retrieves personal purchase history and winnings, draw fetches public draw outcomes, and buy_precheck performs a dry-run gate check. There is no overlap between these four tools.

Naming Consistency4/5

All tool names share the 'lotto_' prefix, creating a consistent namespace. The second part varies between nouns (status, results, draw) and a compound action (buy_precheck), but the pattern is still predictable and readable.

Tool Count5/5

With exactly 4 tools, the server is well-scoped and each tool serves a distinct function. There are no redundant or superfluous tools, and the count is within the ideal range for a focused utility.

Completeness4/5

The tool set covers the core read-only and pre-check operations relevant to a lottery assistant: status, personal results, public draws, and purchase pre-flight. A actual purchase or cancellation tool is absent, but that appears intentional given the server's pre-check focus, so no major gaps block the intended workflow.

Maintenance

ActivitySlowing
ResponsivenessNo issues