CommuteHell
by soyunRyu
README.md
# CommuteHell
출발지와 도착지의 대중교통 경로, 날씨, 환승 횟수, 도보 부담 등을 종합하여 **퇴근 난이도**를 계산하는 Python MCP 서버입니다.
## 주요 기능
- 항목별 점수를 합산한 퇴근 난이도 제공
- TMAP 대중교통 경로 조회
- 총 이동시간 및 환승 횟수 조회
- 도보시간 및 도보거리 조회
- 기상청 초단기예보 조회
- 기온·습도·강수 기반 불쾌지수 계산
- 주말 및 법정공휴일 확인
## 퇴근 난이도 산정 기준
퇴근 난이도는 총 100점으로 계산합니다.
| 항목 | 최대 점수 | 산정 기준 |
|---|---:|---|
| 이동시간 부담 | 30점 | 70분을 부담도 100%로 계산 |
| 환승 피로도 | 25점 | 환승 4회를 부담도 100%로 계산 |
| 도보 부담 | 20점 | 도보시간 70%, 도보거리 30% 반영 |
| 날씨 불쾌도 | 20점 | 불쾌지수 100을 부담도 100%로 계산 |
| 금요일·공휴일 보정 | 5점 | 평일 5점, 금요일·공휴일 0점 |
```text
퇴근 난이도
= 이동시간 부담 30점
+ 환승 피로도 25점
+ 도보 부담 20점
+ 날씨 불쾌도 20점
+ 금요일·공휴일 보정 5점
```
## 사용 API
### TMAP 대중교통 API
출발지와 도착지 사이의 대중교통 경로를 조회합니다.
| 항목 | 내용 |
|---|---|
| 제공 기관 | SK Open API |
| API | TMAP 대중교통 경로 요약정보 |
| 요청 방식 | `POST` |
| 인증 방식 | `appKey` 요청 헤더 |
| 사용 파일 | `transit.py` |
```text
https://apis.openapi.sk.com/transit/routes/sub/
```
주요 사용 데이터:
- 총 이동시간
- 환승 횟수
- 도보시간
- 도보거리
- 대중교통 요금
### 기상청 초단기예보 API
도착지 좌표를 기준으로 현재와 가까운 시간의 날씨를 조회합니다.
| 항목 | 내용 |
|---|---|
| 제공 기관 | 기상청·공공데이터포털 |
| API | 초단기예보조회 |
| 요청 방식 | `GET` |
| 응답 형식 | XML |
| 사용 파일 | `weather.py` |
```text
https://apis.data.go.kr/1360000/VilageFcstInfoService_2.0/getUltraSrtFcst
```
주요 사용 데이터:
- 기온
- 습도
- 하늘 상태
- 강수 형태 및 강수량
- 풍속
- 낙뢰 정보
위도·경도는 기상청 API에서 사용하는 격자 좌표로 변환하여 요청합니다.
### 한국천문연구원 특일정보 API
오늘이 법정공휴일인지 확인합니다.
| 항목 | 내용 |
|---|---|
| 제공 기관 | 한국천문연구원·공공데이터포털 |
| API | 국경일·공휴일 정보 |
| 요청 방식 | `GET` |
| 사용 파일 | `holiday.py` |
```text
https://apis.data.go.kr/B090041/openapi/service/SpcdeInfoService/getRestDeInfo
```
토요일과 일요일은 API를 호출하지 않고 Python 날짜 정보로 판단합니다.
## 기술 스택
| 구분 | 기술 |
|---|---|
| 언어 | Python |
| 패키지 관리 | uv |
| MCP SDK | `mcp` |
| MCP 전송 방식 | 로컬 stdio, 배포 Streamable HTTP |
| HTTP 통신 | requests |
| XML 변환 | xmltodict |
| 환경변수 관리 | python-dotenv |
| 버전 관리 | Git, GitHub |
## 주요 Python 패키지
| 패키지 | 용도 |
|---|---|
| `mcp[cli]` | MCP 서버와 Tool 구현 |
| `requests` | TMAP·기상청·공휴일 API 호출 |
| `xmltodict` | 기상청 XML 응답을 Python 딕셔너리로 변환 |
| `python-dotenv` | `.env` 파일에서 API 키 로딩 |
| `uv` | 가상환경과 의존성 관리 |
## 시스템 구성
```text
로컬 MCP 클라이언트 ── stdio ───────────────┐
▼
원격 MCP 클라이언트 ── Streamable HTTP ── Railway
│
▼
CommuteHell MCP Server
├─ TMAP 대중교통 API
├─ 기상청 초단기예보 API
├─ 한국천문연구원 공휴일 API
└─ 퇴근 난이도 점수 계산
```
## 실행 환경
- Python 3.14
- MCP Python SDK 2.x
- 로컬 실행: stdio
- Docker/Railway 배포: Streamable HTTP
- 원격 MCP 엔드포인트: /mcp
- 기본 포트: `8000`
- Railway 배포 시: Railway가 제공하는 `PORT` 환경변수 사용
- Railway 배포 URL: https://commutehell-production.up.railway.app/mcp
## 환경변수
| 변수 | 용도 |
|---|---|
| `TMAP_API_KEY` | TMAP 대중교통 API 인증 |
| `OPENDATA_API_KEY` | 기상청 및 공휴일 API 인증 |
```env
TMAP_API_KEY=발급받은_TMAP_키
OPENDATA_API_KEY=발급받은_공공데이터포털_키
```
API 키가 들어 있는 `.env` 파일은 Git에 포함하지 않습니다.
## 프로젝트 구조
```text
commuteHell/
├─ src/
│ └─ commutehell/
│ ├─ __init__.py
│ ├─ commute.py
│ ├─ config.py
│ ├─ transit.py
│ ├─ weather.py
│ ├─ holiday.py
│ └─ scoring.py
├─ .env
├─ .gitignore
├─ .python-version
├─ pyproject.toml
├─ uv.lock
└─ README.md
```
## 함수 호출 구조
```text
Claude CLI
└─ MCP tools/call 요청
└─ commute.py
└─ get_commute_difficulty() [MCP Tool]
├─ transit.py
│ └─ get_transit_route()
│ └─ TMAP API
│
├─ weather.py
│ └─ fetch_weather()
│ ├─ st_forecast()
│ │ ├─ get_wheather_time()
│ │ ├─ convert_to_grid()
│ │ └─ 기상청 API
│ └─ summarize_weather()
│ ├─ calculate_discomfort_index()
│ ├─ get_discomfort_level()
│ ├─ format_precipitation()
│ ├─ add_unit()
│ └─ make_weather_summary()
│
├─ holiday.py
│ └─ get_day_off_info()
│ └─ 공휴일 API
│
└─ scoring.py
├─ calculate_travel_time_score()
│ └─ clamp()
├─ calculate_transfer_fatigue()
│ └─ clamp()
├─ calculate_walking_burden()
│ └─ clamp()
├─ calculate_weather_discomfort()
│ └─ clamp()
└─ calculate_commute_difficulty()
└─ calculate_day_score()
```
## 파일별 역할
| 파일 | 역할 |
|---|---|
| `commute.py` | MCP 서버 실행 및 공개 Tool 관리 |
| `config.py` | 환경변수, API 키, API URL 관리 |
| `transit.py` | TMAP 대중교통 경로 조회 |
| `weather.py` | 기상청 예보 조회 및 불쾌지수 계산 |
| `holiday.py` | 주말 및 법정공휴일 확인 |
| `scoring.py` | 항목별 점수 및 최종 퇴근 난이도 계산 |
## Remote MCP Server
배포된 Streamable HTTP MCP 서버를 사용할 수 있습니다.
```text
https://commutehell-production.up.railway.app/mcp
```
예를 들어 Gemini CLI에 다음과 같이 등록합니다.
```
gemini mcp add --transport http commuteHell https://commutehell-production.up.railway.app/mcp
```
## MCP Tool
### `get_commute_difficulty`
출발지와 도착지 좌표를 받아 퇴근 난이도를 계산합니다.
#### 입력값
| 이름 | 형식 | 설명 |
|---|---|---|
| `start_lat` | `float` | 출발지 위도 |
| `start_lon` | `float` | 출발지 경도 |
| `end_lat` | `float` | 도착지 위도 |
| `end_lon` | `float` | 도착지 경도 |
#### 호출 예시
```json
{
"start_lat": 37.5569,
"start_lon": 126.8643,
"end_lat": 37.5503,
"end_lon": 126.9158
}
```
사용자가 지역명이나 주소를 입력하면 MCP 클라이언트가 먼저 좌표를 찾은 뒤 이 Tool을 호출합니다.
## 참고 자료
- [MCP 블로그 시리즈](https://toyourlight.tistory.com/category/%F0%9F%97%9C%20MCP?page=4)
- [MCP에 대한 이해와 나만의 미니 프로젝트 만들기](https://velog.io/@cjungy2/MCP%EC%97%90-%EB%8C%80%ED%95%9C-%EC%9D%B4%ED%95%B4%EC%99%80-%EB%82%98%EB%A7%8C%EC%9D%98-mini-Project-%EB%A7%8C%EB%93%A4%EA%B8%B0)
- [Model Context Protocol Quickstart](https://glama.ai/blog/2024-11-25-model-context-protocol-quickstart)
- [Awesome MCP Servers](https://github.com/punkpeye/awesome-mcp-servers)
- [WikiDocs MCP 자료](https://wikidocs.net/286574)
TDQS
B3.4/5.0
Scored across 1 tool
Disambiguation5/5
With only one tool, there is no possibility of confusion or misselection. The purpose of get_commute_difficulty is clear and distinct.
Naming Consistency5/5
The single name follows the snake_case verb_noun pattern (get_commute_difficulty), which is conventional and consistent by default.
Tool Count2/5
The server has only one tool, which is too few for the apparent domain of 'CommuteHell'. A single calculation function feels more like a standalone service than a coherent MCP tool set.
Completeness3/5
The tool covers the core operation of calculating difficulty but misses auxiliary capabilities such as retrieving traffic details, comparing route options, or explaining contributing factors. This creates notable gaps for a comprehensive commute analysis.
Maintenance
ActivitySlowing
ResponsivenessNo issues