# AGENTS.md — Korea Transit MCP 프로젝트 AI 가이드

## 프로젝트 개요

**Korea Transit MCP** — 한국 대중교통 MCP 서버
- FastMCP 기반 (공식 SDK)
- ODsay LAB + 서울시 공공데이터 API 조합
- 경로 검색, 실시간 도착 정보, 혼잡도, 첫차/막차 조회 제공

## 아키텍처

```
src/korea_transit_mcp_server/
├── server.py           # MCP 서버 & 도구 정의 (한글 docstring)
├── config.py           # API 키 관리, TTL 캐시 (SimpleCache)
├── clients/
│   ├── odsay.py        # ODsay LAB API 클라이언트 (경로 검색, 정류장/역 검색)
│   └── public_data.py  # 서울시 버스·지하철 실시간 정보 API
└── tools/
    ├── route.py        # 경로 검색 & 노선 정보 포매팅
    ├── bus.py          # 실시간 버스 도착 & 위치 추적
    ├── subway.py       # 실시간 지하철 도착 & 혼잡도 & 첫차/막차
    └── search.py       # 정류장/역 검색 & 주변 역 찾기
```

## 주요 패턴

### 1. API 클라이언트 구조

```python
# clients/*.py의 함수들은 Raw API 응답 반환 (JSON/딕셔너리)
async def get_bus_arrival(ars_id: str) -> list[dict[str, str]]:
    """Raw API 응답 반환"""

# tools/*.py의 함수들은 사용자 친화적 문자열 반환
async def get_bus_arrival(ars_id: str) -> str:
    """포매팅된 텍스트 반환"""
```

### 2. 캐싱 전략 (TTL 기반)

```python
CACHE_TTL_ROUTE = 600      # 10분 — 경로는 자주 변하지 않음
CACHE_TTL_STATION = 86400  # 1일 — 정류장/역 정보는 거의 고정
CACHE_TTL_CONGESTION = 1800 # 30분 — 혼잡도는 통계 데이터
# 실시간 도착 정보는 캐시하지 않음
```

### 3. API 호출 구조

모든 클라이언트 함수는:
1. API 키 확인 → 없으면 RuntimeError
2. 일일 호출 한도 확인 (ODsay만)
3. 캐시 확인 (해당하는 경우)
4. API 요청 (httpx.AsyncClient, timeout=10~15초)
5. 응답 파싱 & 검증
6. 캐시 저장 (해당하는 경우)

## 패키지 구조

### pyproject.toml 설정
```toml
[project.scripts]
korea-transit-mcp-server = "korea_transit_mcp_server.server:main"  # 진입점

[tool.hatch.build.targets.wheel]
only-include = ["src/korea_transit_mcp_server"]
sources = ["src"]  # src 레이아웃 지원
```

### MCP 설정 파일 패턴
- `.mcp.json.example`: API 키 없는 템플릿 (Git에 포함)
- `.mcp.json`: 실제 API 키 포함 (`.gitignore`에 추가)
- 사용자는 example을 복사하여 API 키 입력

## 코딩 컨벤션

### 언어
- **모든 docstring & 주석**: 한글 사용
- **type hints & 변수명**: 영어 유지
- **에러 메시지**: 한글

### 함수 설계
- **비동기**: 모든 API 호출은 `async def`
- **반환값**: Raw API 응답 (clients) 또는 포매팅된 텍스트 (tools)
- **예외**: RuntimeError만 사용 (API 키 부재, 호출 한도 초과 등)

### 모듈 docstring
```python
"""명확한 설명 — 기능 요약."""
```

## 파일 수정 가이드

### clients/ 수정 시
- `_get()`, `_parse_*()` 등 private 함수 주의
- 캐시 키 형식 변경 금지 (기존 캐시 무효화 위험)
- API URL 상수는 수정 금지

### tools/ 수정 시
- `_format_*()` 함수들의 출력 형식이 중요 (사용자가 직접 봄)
- 포매팅 함수는 raw 딕셔너리를 문자열로 변환
- 이모지 사용 일관되게 (🚌 버스, 🚇 지하철, 🚊 역 등)

### server.py 수정 시
- MCP 도구는 `@mcp.tool()` 데코레이터 필수
- 도구 파라미터 이름과 타입은 매우 중요 (Claude가 파싱함)
- docstring의 Args 섹션은 필수

## API 키 설정

### 로컬 개발 환경
```bash
export ODSAY_API_KEY="your_key"
export SEOUL_BUS_API_KEY="your_key"
export SEOUL_SUBWAY_API_KEY="your_key"
```

### MCP 클라이언트 (Claude Code)

**개발 환경** — 프로젝트 `.mcp.json`:
1. `.mcp.json.example`을 `.mcp.json`으로 복사
2. API 키 입력
3. `.mcp.json`은 `.gitignore`에 포함 (보안)

```json
{
  "mcpServers": {
    "korea-transit": {
      "type": "stdio",
      "command": "uv",
      "args": ["run", "korea-transit-mcp-server"],
      "env": {
        "ODSAY_API_KEY": "your_key",
        "SEOUL_BUS_API_KEY": "your_key",
        "SEOUL_SUBWAY_API_KEY": "your_key"
      }
    }
  }
}
```

**일반 사용자** (PyPI 배포 후) — 전역 `~/.claude.json`:
```json
{
  "mcpServers": {
    "korea-transit": {
      "command": "uvx",
      "args": ["korea-transit-mcp-server"],
      "env": {
        "ODSAY_API_KEY": "your_key",
        "SEOUL_BUS_API_KEY": "your_key",
        "SEOUL_SUBWAY_API_KEY": "your_key"
      }
    }
  }
}
```

## 테스트 실행

```bash
# 환경 설정 및 의존성 설치
uv sync --group dev

# 테스트 실행
PYTHONPATH=src uv run python -m pytest -v

# 커버리지 확인 (설정되어 있으면)
# uv run pytest --cov
```

**현재 상태**: 18개 테스트 모두 통과 ✅

## 문제 해결

### "ODSAY_API_KEY 환경변수가 설정되지 않았습니다"
→ 환경 변수 확인, MCP 설정의 env 확인

### API 응답 파싱 에러
→ `_parse_bus_xml()`, `_format_*()` 함수 확인
→ API 응답 형식이 변경되었을 가능성

### 캐시 관련 버그
→ `config.cache.clear()` (모든 캐시 초기화)
→ TTL 값 확인

## 성능 고려사항

- **API 호출**: 캐싱으로 불필요한 호출 방지
- **비동기**: httpx.AsyncClient 사용으로 I/O 최적화

## 추가 자료

- [FastMCP 공식 문서](https://github.com/jlowin/fastmcp)
- [ODsay LAB API](https://lab.odsay.com/)
- [서울시 공공데이터](https://data.seoul.go.kr/)
