subway-congestion-mcp
by jinjukimink
README.md
# π μ§νμ² νΌμ‘λ μ리미 MCP μλ²
> μμΈ μ§νμ² 1~8νΈμ μ νΌμ‘λ μ 보λ₯Ό μ 곡νλ Model Context Protocol (MCP) μλ²
μΆν΄κ·Ό μκ°λμ μ§νμ² μ΄ μΌλ§λ νΌμ‘νμ§ λ―Έλ¦¬ μκ³ , κ°μ₯ μΎμ ν μκ°μ μ΄λν μ μλλ‘ λμμ£Όλ λλν λΉμμ
λλ€.
---
## π λͺ©μ°¨
- [μ£Όμ κΈ°λ₯](#-μ£Όμ-κΈ°λ₯)
- [μ μ΄ νλ‘μ νΈμΈκ°?](#-μ-μ΄-νλ‘μ νΈμΈκ°)
- [μμνκΈ°](#-μμνκΈ°)
- [μ¬μ© λ°©λ²](#-μ¬μ©-λ°©λ²)
- [ν΄λ ꡬ쑰](#-ν΄λ-ꡬ쑰)
- [κ°λ° κ°μ΄λ](#-κ°λ°-κ°μ΄λ)
- [λ¬Έμ ν΄κ²°](#-λ¬Έμ -ν΄κ²°)
---
## β¨ μ£Όμ κΈ°λ₯
### 1. π κ²½λ‘ κ²μ
```
"κ°λ¨μμμ μμΌμ κ°λ €λλ°"
β μ΅μ κ²½λ‘ + νΌμ‘λ μ 보 + λμ κ²½λ‘ 2κ° μ μ
```
### 2. π μ νΌμ‘λ μ‘°ν
```
"κ°λ¨μ μ§κΈ νΌμ‘ν΄?"
β μ€μκ° νΌμ‘λ + λ€μ μ΄μ°¨ λμ°© μκ°
```
### 3. β° λμ°© μκ° κ³μ°
```
"κ°λ¨μμ μμΌκΉμ§ 9μκΉμ§ λμ°©ν΄μΌ ν΄"
β νμν μΆλ° μκ° + κ·Έ μκ°λ νΌμ‘λ
```
### 4. π μ΄μ°¨ λμ°© μ 보
```
"κ°λ¨μ μ΄μ°¨ μΈμ μ?"
β λ€μ 3κ° μ΄μ°¨ λμ°© μκ° + κ° νΌμ‘λ
```
### 5. π μ§νμ² μ΄μ κ²μ
```
"2νΈμ μ§κΈ μ§μ°λΌ?"
β μ΅μ μ΄ν μ΄μ, κ³ μ₯, μμ μ 보
```
---
## π― μ μ΄ νλ‘μ νΈμΈκ°?
### κΈ°μ‘΄ μ±μ νκ³
- λ€μ΄λ²/μΉ΄μΉ΄μ€λ§΅: νΌμ‘λ μ 보 μμ
- μμΈκ΅ν΅κ³΅μ¬ μ±: μ€μκ° μ λ³΄λ§ μ 곡, μμΈ‘ λΆκ°
### μ°λ¦¬μ μ°¨λ³μ
β
**ν΅κ³ κΈ°λ° μμΈ‘**: κ³Όκ±° λ°μ΄ν°λ‘ μκ°λλ³ νΌμ‘λ μμΈ‘
β
**μμ¬κ²°μ μ§μ**: "μ§κΈ vs 30λΆ ν" λΉκ΅ μ 곡
β
**λνν μΈν°νμ΄μ€**: Claudeμ μμ°μ΄λ‘ μν΅
β
**λμ μ μ**: λ¨μ μ 보 β μ΅μ μ ν μΆμ²
---
## π μμνκΈ°
### νμ μꡬμ¬ν
- **Python** 3.10 μ΄μ
- **pip** λλ **uv** (κΆμ₯)
- **Claude Desktop** (MCP ν΄λΌμ΄μΈνΈ)
- API ν€ 2κ° (무λ£, νμ):
- ODsay API Key
- μμΈμ Open API Key
- (μ ν) Kakao REST API Key
---
### μ€μΉ λ°©λ²
#### 1λ¨κ³: μ μ₯μ 볡μ
```bash
git clone https://github.com/yourusername/subway-congestion-mcp.git
cd subway-congestion-mcp
```
#### 2λ¨κ³: κ°μνκ²½ μμ± (κΆμ₯)
```bash
# uv μ¬μ© (κΆμ₯ - λΉ λ¦)
uv venv
source .venv/bin/activate # Mac/Linux
# λλ
.venv\Scripts\activate # Windows
# λλ μΌλ° venv
python -m venv .venv
source .venv/bin/activate
```
#### 3λ¨κ³: μμ‘΄μ± μ€μΉ
```bash
# uv μ¬μ©
uv pip install -e .
# λλ pip μ¬μ©
pip install -e .
```
#### 4λ¨κ³: νκ²½ μ€μ
```bash
cp .env.example .env
# .env νμΌμ μ΄μ΄μ API ν€ μ
λ ₯
```
#### 5λ¨κ³: λ°μ΄ν° λ€μ΄λ‘λ
```bash
python scripts/download_data.py
```
#### 6λ¨κ³: ν
μ€νΈ
```bash
pytest
```
#### 7λ¨κ³: μ€ν
```bash
# κ°μνκ²½ νμ±ν μνμμ μ€ν
python -m subway_congestion_mcp
```
---
### API ν€ λ°κΈ
#### 1. ODsay API (λμ€κ΅ν΅ κ²½λ‘ κ²μ)
**λ°κΈ μ¬μ΄νΈ**: https://lab.odsay.com
1. νμκ°μ
(무λ£)
2. "API μ¬μ© μ μ²" ν΄λ¦
3. μ± μ 보 μ
λ ₯:
- μ± μ΄λ¦: `μ§νμ² νΌμ‘λ μ리미`
- μ¬μ© λͺ©μ : `κ°μΈ νλ‘μ νΈ`
- νλ«νΌ: `Web/Mobile`
4. API ν€ λ³΅μ¬ β `.env` νμΌμ `ODSAY_API_KEY`μ λΆμ¬λ£κΈ°
**μ ν**: ν루 1,000건 (무λ£)
---
#### 2. μμΈμ Open API (μ€μκ° λμ°© μ 보)
**λ°κΈ μ¬μ΄νΈ**: https://data.seoul.go.kr
1. νμκ°μ
(무λ£)
2. μλ¨ λ©λ΄ "μΈμ¦ν€ μ μ²" ν΄λ¦
3. "νμ© μ μ²" λ²νΌ ν΄λ¦
4. μΈμ¦ν€ λ³΅μ¬ β `.env` νμΌμ `SEOUL_API_KEY`μ λΆμ¬λ£κΈ°
**μ ν**: ν루 1,000건 (무λ£)
---
### μ€μ
#### `.env` νμΌ μμ
```bash
# ODsay API (νμ)
ODSAY_API_KEY=your_odsay_api_key_here
# μμΈμ Open API (νμ)
SEOUL_API_KEY=your_seoul_api_key_here
# λ‘κ·Έ λ 벨 (DEBUG, INFO, WARNING, ERROR)
LOG_LEVEL=INFO
# μΊμ TTL (μ΄)
CACHE_TTL=300
```
#### Claude Desktop μ€μ
Mac: `~/Library/Application Support/Claude/claude_desktop_config.json`
Windows: `%APPDATA%/Claude/claude_desktop_config.json`
```json
{
"mcpServers": {
"subway-congestion": {
"command": "uv",
"args": [
"--directory",
"/μ λκ²½λ‘/subway-congestion-mcp",
"run",
"subway-congestion-mcp"
]
}
}
}
```
**λλ μΌλ° Python μ¬μ© μ:**
```json
{
"mcpServers": {
"subway-congestion": {
"command": "python",
"args": [
"-m",
"subway_congestion_mcp"
],
"env": {
"PYTHONPATH": "/μ λκ²½λ‘/subway-congestion-mcp/src"
}
}
}
}
```
μ€μ ν Claude Desktopμ μ¬μμνμΈμ.
---
## π¬ μ¬μ© λ°©λ²
### μμ 1: κ²½λ‘ κ²μ
```
μ¬μ©μ: "κ°λ¨μμμ μμΌμ κ°λ €λλ°"
Claude:
π μ΅μ κ²½λ‘: 2νΈμ μμ μ§ν΅ (1μ, 2λΆ)
β’ κ°λ¨μ: νΌμ‘λ 152% (λ§€μ°νΌμ‘) β
β’ μμΌμ: νΌμ‘λ 148% (νΌμ‘) π΄
π λμ κ²½λ‘:
1. 9νΈμ νμΉ (12λΆ) - νΌμ‘λ μ 보 μμ
2. 146λ² λ²μ€ (15λΆ) - νΌμ‘λ μ 보 μμ
π‘ 2νΈμ μ΄ κ°μ₯ λΉ λ₯΄μ§λ§ νμ¬ λ§€μ° νΌμ‘ν©λλ€.
```
---
## π ν΄λ ꡬ쑰
```
subway-congestion-mcp/
βββ README.md # π μ§κΈ λ³΄κ³ μλ νμΌ
βββ pyproject.toml # νλ‘μ νΈ λ©νλ°μ΄ν° λ° μμ‘΄μ±
βββ .env.example # νκ²½ λ³μ ν
νλ¦Ώ
βββ .gitignore # Git μ μΈ νμΌ
β
βββ src/
β βββ subway_congestion_mcp/ # λ©μΈ ν¨ν€μ§
β βββ __init__.py
β βββ __main__.py # MCP μλ² μ§μ
μ
β β
β βββ server.py # MCP μλ² μ€μ
β β
β βββ tools/ # MCP λꡬ μ μ
β β βββ __init__.py
β β βββ search_route.py # κ²½λ‘ κ²μ
β β βββ get_station_congestion.py # μ νΌμ‘λ μ‘°ν
β β βββ get_route_congestion.py # κ²½λ‘ νΌμ‘λ + μκ° κ³μ°
β β βββ get_train_arrival.py # μ΄μ°¨ λμ°© μ 보
β β βββ search_issues.py # μ§νμ² μ΄μ κ²μ
β β
β βββ services/ # λΉμ¦λμ€ λ‘μ§
β β βββ __init__.py
β β βββ odsay_service.py # ODsay API νΈμΆ
β β βββ seoul_metro_service.py # μμΈμ API νΈμΆ
β β βββ congestion_service.py # νΌμ‘λ κ³μ° λ‘μ§
β β βββ cache_service.py # μΊμ± λ μ΄μ΄
β β
β βββ data/ # λ°μ΄ν° μ²λ¦¬
β β βββ __init__.py
β β βββ loader.py # CSV λ°μ΄ν° λ‘λ
β β βββ parser.py # λ°μ΄ν° νμ±
β β βββ validator.py # λ°μ΄ν° κ²μ¦
β β
β βββ utils/ # μ νΈλ¦¬ν°
β β βββ __init__.py
β β βββ logger.py # λ‘κΉ
μ€μ
β β βββ time_utils.py # μκ° λ³ν
β β βββ error_handler.py # μλ¬ νΈλ€λ§
β β βββ constants.py # μμ μ μ
β β
β βββ models/ # λ°μ΄ν° λͺ¨λΈ (Pydantic)
β βββ __init__.py
β βββ route.py # κ²½λ‘ κ΄λ ¨ λͺ¨λΈ
β βββ station.py # μ κ΄λ ¨ λͺ¨λΈ
β βββ congestion.py # νΌμ‘λ κ΄λ ¨ λͺ¨λΈ
β
βββ data/ # μ μ λ°μ΄ν°
β βββ congestion/ # νΌμ‘λ CSV νμΌ
β β βββ line1.csv
β β βββ line2.csv
β β βββ ...
β βββ stations.json # μ μ 보 (μ’ν, λ
Έμ λ±)
β
βββ tests/ # ν
μ€νΈ μ½λ
β βββ __init__.py
β βββ test_services/ # μλΉμ€ ν
μ€νΈ
β β βββ test_odsay_service.py
β β βββ test_congestion_service.py
β βββ test_tools/ # λꡬ ν
μ€νΈ
β βββ test_search_route.py
β βββ test_get_station_congestion.py
β
βββ scripts/ # μ νΈλ¦¬ν° μ€ν¬λ¦½νΈ
β βββ download_data.py # CSV λ°μ΄ν° λ€μ΄λ‘λ
β βββ validate_env.py # νκ²½ λ³μ κ²μ¦
β
βββ logs/ # λ‘κ·Έ νμΌ (gitignore)
```
---
## π κΈ°μ μ€ν
### ν΅μ¬ κΈ°μ
- **Python 3.10+** - λ°νμ
- **MCP SDK** - Model Context Protocol
- **Pydantic** - λ°μ΄ν° κ²μ¦
- **pandas** - CSV λ°μ΄ν° μ²λ¦¬
### μ£Όμ λΌμ΄λΈλ¬λ¦¬
```toml
[project]
dependencies = [
"mcp>=0.9.0", # MCP SDK
"pydantic>=2.0.0", # λ°μ΄ν° κ²μ¦
"pandas>=2.0.0", # CSV μ²λ¦¬
"requests>=2.31.0", # HTTP ν΄λΌμ΄μΈνΈ
"python-dateutil>=2.8.2", # λ μ§/μκ° μ²λ¦¬
"python-dotenv>=1.0.0", # νκ²½ λ³μ
]
[project.optional-dependencies]
dev = [
"pytest>=7.4.0", # ν
μ€νΈ νλ μμν¬
"pytest-asyncio>=0.21.0", # λΉλκΈ° ν
μ€νΈ
"black>=23.0.0", # μ½λ ν¬λ§·ν
"ruff>=0.1.0", # λ¦°ν
"mypy>=1.5.0", # νμ
체νΉ
]
```
---
## π¨βπ» κ°λ° κ°μ΄λ
### κ°λ° μλ² μ€ν
```bash
# MCP Inspectorλ‘ ν
μ€νΈ
npx @modelcontextprotocol/inspector uv --directory . run subway-congestion-mcp
# λλ μ§μ μ€ν
python -m subway_congestion_mcp
```
### ν
μ€νΈ
```bash
# μ 체 ν
μ€νΈ
pytest
# νΉμ νμΌ ν
μ€νΈ
pytest tests/test_services/test_congestion_service.py
# 컀λ²λ¦¬μ§ νμΈ
pytest --cov=subway_congestion_mcp --cov-report=html
```
### μ½λ νμ§
```bash
# ν¬λ§·ν
black src/
# λ¦°ν
ruff check src/
# νμ
체νΉ
mypy src/
```
### λ‘κ·Έ νμΈ
```bash
# μ€μκ° λ‘κ·Έ
tail -f logs/app.log
# μλ¬ λ‘κ·Έλ§
tail -f logs/error.log
```
---
## π λ¬Έμ ν΄κ²°
### Q1. MCP μλ²κ° Claude Desktopμ μ°κ²°λμ§ μμμ
**νμΈ μ¬ν**:
1. `claude_desktop_config.json` κ²½λ‘κ° μ ννκ°?
2. μ λ κ²½λ‘λ₯Ό μ¬μ©νλκ°?
3. κ°μνκ²½μ΄ νμ±νλμ΄ μλκ°?
4. Claude Desktopμ μ¬μμνλκ°?
**λλ²κΉ
**:
```bash
# μ§μ μ€νν΄λ³΄κΈ°
cd /μ λκ²½λ‘/subway-congestion-mcp
source .venv/bin/activate
python -m subway_congestion_mcp
# λ‘κ·Έ νμΈ
cat ~/Library/Logs/Claude/mcp*.log
```
---
### Q2. API νΈμΆμ΄ μ€ν¨ν΄μ
**νμΈ μ¬ν**:
1. `.env` νμΌμ API ν€κ° μ νν μ
λ ₯λμλκ°?
2. API ν€κ° μ ν¨νκ°?
3. μΈν°λ· μ°κ²°μ΄ μ μμΈκ°?
**λλ²κΉ
**:
```python
# μ€ν¬λ¦½νΈλ‘ API ν€ ν
μ€νΈ
python scripts/test_api_keys.py
```
---
### Q3. νΌμ‘λ λ°μ΄ν°κ° μλ€κ³ λμμ
**νμΈ μ¬ν**:
1. CSV λ°μ΄ν°λ₯Ό λ€μ΄λ‘λνλκ°?
```bash
python scripts/download_data.py
```
2. `data/congestion/` ν΄λμ νμΌμ΄ μλκ°?
3. 1~8νΈμ μμΈκ°? (9νΈμ λ―Έμ§μ)
---
## π μ±λ₯ μ΅μ ν
### μΊμ± μ λ΅
- **νΌμ‘λ λ°μ΄ν°**: λ©λͺ¨λ¦¬ μΊμ (pandas DataFrame)
- **μ€μκ° λμ°© μ 보**: 30μ΄ TTL
- **κ²½λ‘ κ²μ κ²°κ³Ό**: 10λΆ TTL
### μλ¬ νΈλ€λ§
```python
# Retry λ°μ½λ μ΄ν°
@retry(max_attempts=3, backoff=exponential)
async def call_api():
...
# Fallback μ λ΅
try:
return await get_realtime_data()
except APIError:
return get_statistical_data() # ν΅κ³ λ°μ΄ν°λ‘ λ체
```
---
## π 보μ
- β
API ν€λ `.env` νμΌμλ§ μ μ₯νκ³ `.gitignore`λ‘ μ»€λ° μ μΈ
- β
ν€ μ μΆ μ μ¦μ μ¬λ°κΈ λ° νκΈ°
- β
μν μ€μ μ `.env.example`λ§ κ³΅μ
- β
λ―Όκ°ν μ 보λ λ‘κ·Έμ λ¨κΈ°μ§ μμ
- β
μμ‘΄μ±μ `pyproject.toml`μμ κ΄λ¦¬
---
## π λ‘λλ§΅
### v1.0 (νμ¬) - MVP
- β
κΈ°λ³Έ 5κ°μ§ λꡬ ꡬν
- β
1~8νΈμ μ§μ
- β
ν΅κ³ κΈ°λ° νΌμ‘λ
### v1.1 (μμ )
- β¬ 9νΈμ , μ λΆλΉμ μ§μ
- β¬ λμ μ μΆμ²
- β¬ μΉ μΈν°νμ΄μ€
### v2.0 (κ³ν)
- β¬ κ°μΈν κΈ°λ₯
- β¬ μλ¦Ό μ€μ
- β¬ ν΅κ³ λμ보λ
---
## π€ κΈ°μ¬νκΈ°
1. Fork the repository
2. Create your feature branch (`git checkout -b feature/AmazingFeature`)
3. Commit your changes (`git commit -m 'Add some AmazingFeature'`)
4. Push to the branch (`git push origin feature/AmazingFeature`)
5. Open a Pull Request
---
## π λΌμ΄μΌμ€
MIT License - μμ λ‘κ² μ¬μ©νμΈμ!
---
## π¬ μ°λ½μ²
λ¬Έμμ¬νμ΄λ μ μμ¬νμ΄ μμΌμλ©΄:
- GitHub Issues
- Email: kimjinjuind@gmail.com
---
**Made with β€οΈ for Seoul Subway Commuters**
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues