
garmin-mcp
Garmin Connect ๋ฌ๋ ๋ฐ์ดํฐ๋ฅผ LLM์ ์ ๊ณตํ๋ MCP(Model Context Protocol) ์๋ฒ์ ๋๋ค.
Claude Desktop ๋ฑ MCP ํด๋ผ์ด์ธํธ์ ์ฐ๋ํ์ฌ ๋ฌ๋ ํ๋ จ ๋ถ์, ๊ณํ ์๋ฆฝ, ์ํฌ์์ ์์ฑ ๋ฑ์ ์ํํ ์ ์์ต๋๋ค.
์ฃผ์ ๊ธฐ๋ฅ
- ๋ฌ๋ ํ๋ ์กฐํ - ์ต๊ทผ ํ๋, ๋ ์ง๋ณ ์กฐํ, ์์ธ ๋ถ์, ์คํ๋ฆฟ ๋ฐ์ดํฐ (47๊ฐ ํ๋: ํ์ด์ค, ์ฌ๋ฐ, ์ผ์ด๋์ค, ๋ฌ๋ ๋ค์ด๋๋ฏน์ค, ํ์, HR์กด, GAP, ๊ฒฝ์ฌ๋, ์คํ๋ฏธ๋, ์จ๋ ๋ฑ)
- ํธ๋ ์ผ๋ฌ๋ ๋ถ์ - ClimbPro ๊ฒฝ์ฌ ๊ตฌ๊ฐ ๋ถ์, ๋ฑ๊ธ๋ณ ๋์ด๋, Grade Adjusted Pace, Run/Walk Detection, ๋ ์จ ์กฐ๊ฑด
- ์ฃผ๊ฐ/์๊ฐ ์์ฝ - ๋ณผ๋ฅจ ํธ๋ ๋, ์ ์ ๋๋น ๋น๊ต
- ํ๋ จ ์งํ - VO2max, ํ๋ จ ์ํ, ํ๋ จ ์ค๋น๋, ๋ ์ด์ค ์์ธก, ์ ์ฐ์ญ์น
- ์ฌ๋ฐ/HRV - ์ผ๊ฐ ์ฌ๋ฐ, ์ฌ๋ฐ๋ณ์ด๋, ํ๋๋ณ ์ฌ๋ฐ์กด ๋ถํฌ
- ์ฐ๋์ค - ์๋ฉด, ์คํธ๋ ์ค, ๋ฐ๋๋ฐฐํฐ๋ฆฌ, SpO2
- ๊ฐ์ธ ๊ธฐ๋ก/๋ชฉํ - PR, ํผํธ๋์ค ๋ชฉํ
- ์ํฌ์์ ์์ฑ - ์๊ฐ/๊ฑฐ๋ฆฌ ๊ธฐ๋ฐ ์ธํฐ๋ฒ, ํ ํฌ ๋ฑ ๊ตฌ์กฐํ๋ ์ํฌ์์์ Garmin ์์น์ ์ ์ก (ํ์ด์ค/์ฌ๋ฐ/์ผ์ด๋์ค/ํ์ ํ๊ฒ)
- ๋ฌ๋ํ ๊ด๋ฆฌ - ์ ๋ฐ๋ณ ๋์ ๊ฑฐ๋ฆฌ + ๋ง๋ชจ์จ ์ถ์
- ๊ฐ์ธ์ ๋ณด ๋ณดํธ - ๋ชจ๋ API ์๋ต์์ PII(์์ ์ ์ด๋ฆ, ํ๋กํ ID, GPS ์ขํ) ์๋ ํํฐ๋ง
์๊ตฌ ์ฌํญ
- Python 3.10+
- uv ํจํค์ง ๋งค๋์
- Garmin Connect ๊ณ์
์ค์น
git clone https://github.com/leewnsdud/garmin-connect-mcp.git
cd garmin-connect-mcp
uv sync์ธ์ฆ
์ต์ด 1ํ ์ธ์ฆ์ด ํ์ํฉ๋๋ค.
uv run python scripts/auth.py์ด๋ฉ์ผ, ๋น๋ฐ๋ฒํธ๋ฅผ ์
๋ ฅํ๋ฉด OAuth ํ ํฐ์ด ~/.garminconnect/์ ์ ์ฅ๋ฉ๋๋ค. MFA ์ฌ์ฉ ์ ์ฝ๋ ์
๋ ฅ ํ๋กฌํํธ๊ฐ ๋ํ๋ฉ๋๋ค.
ํ ํฐ์ด ๋ง๋ฃ๋๋ฉด ๋ค์ ์คํํ๊ฑฐ๋,
.envํ์ผ์ ์๊ฒฉ์ฆ๋ช ์ ์ค์ ํ๋ฉด ์๋ ๊ฐฑ์ ๋ฉ๋๋ค.
Claude Desktop ์ฐ๋
~/Library/Application Support/Claude/claude_desktop_config.json์ ์ถ๊ฐ:
{
"mcpServers": {
"garmin-mcp": {
"command": "/Users/<username>/.local/bin/uv",
"args": [
"--directory",
"/path/to/garmin-mcp",
"run",
"garmin-mcp"
]
}
}
}
uv์ ์ ์ฒด ๊ฒฝ๋ก๋ฅผ ์ฌ์ฉํด์ผ ํฉ๋๋ค.which uv๋ก ํ์ธํ์ธ์.
์ค์ ํ Claude Desktop์ ์ฌ์์ํ๋ฉด ๋๊ตฌ๊ฐ ํ์ฑํ๋ฉ๋๋ค.
ํ๊ฒฝ ๋ณ์ (์ ํ)
.env.example์ ์ฐธ๊ณ ํ์ฌ .env ํ์ผ์ ์์ฑํฉ๋๋ค.
| ๋ณ์ | ์ค๋ช | ๊ธฐ๋ณธ๊ฐ |
|---|---|---|
GARMIN_EMAIL | Garmin Connect ์ด๋ฉ์ผ | - |
GARMIN_PASSWORD | Garmin Connect ๋น๋ฐ๋ฒํธ | - |
GARMIN_TOKEN_DIR | ํ ํฐ ์ ์ฅ ๊ฒฝ๋ก | ~/.garminconnect |
GARMINTOKENS | Base64 ์ธ์ฝ๋ฉ ํ ํฐ (CI/Docker์ฉ) | - |
์ ๊ณต ๋๊ตฌ (24๊ฐ)
Activities
| ๋๊ตฌ | ์ค๋ช | ์ฃผ์ ํ๋ผ๋ฏธํฐ |
|---|---|---|
get_recent_activities | ์ต๊ทผ ๋ฌ๋ ํ๋ ๋ชฉ๋ก (GAP, RWD ํฌํจ) | count (๊ธฐ๋ณธ 20, ์ต๋ 100) |
get_activities_by_date | ๋ ์ง ๋ฒ์๋ก ๋ฌ๋ ํ๋ ์กฐํ | start_date, end_date |
get_activity_detail | ํ๋ ์์ธ ์ ๋ณด (์คํ๋ฏธ๋, ์ํฉํธ ๋ก๋ ํฌํจ) | activity_id |
get_activity_splits | km๋ณ ์คํ๋ฆฟ ๋ฐ์ดํฐ | activity_id |
get_activity_weather | ํ๋ ์ค ๋ ์จ ์กฐ๊ฑด (์จ๋, ์ต๋, ํ์) | activity_id |
get_activity_typed_splits | ClimbPro ๊ฒฝ์ฌ ๊ตฌ๊ฐ ๋ถ์ (๋ฑ๊ธ, GAP) | activity_id |
Summary
| ๋๊ตฌ | ์ค๋ช | ์ฃผ์ ํ๋ผ๋ฏธํฐ |
|---|---|---|
get_weekly_running_summary | ์ฃผ๊ฐ ๋ฌ๋ ์์ฝ | end_date, weeks (์ต๋ 12) |
get_monthly_running_summary | ์๊ฐ ๋ฌ๋ ์์ฝ + ์ ์ ๋น๊ต | year, month |
Training
| ๋๊ตฌ | ์ค๋ช | ์ฃผ์ ํ๋ผ๋ฏธํฐ |
|---|---|---|
get_training_status | ํ๋ จ ์ํ | date |
get_training_readiness | ํ๋ จ ์ค๋น๋ ์ ์ | date |
get_vo2max_and_fitness | VO2max + ํผํธ๋์ค ๋์ด | date |
get_race_predictions | 5K/10K/ํํ/ํ ์์ ๊ธฐ๋ก | ์์ |
get_lactate_threshold | ์ ์ฐ์ญ์น ์ฌ๋ฐ/ํ์ด์ค | start_date, end_date |
Heart Rate
| ๋๊ตฌ | ์ค๋ช | ์ฃผ์ ํ๋ผ๋ฏธํฐ |
|---|---|---|
get_heart_rate_data | ์ผ๊ฐ ์ฌ๋ฐ ๋ฐ์ดํฐ | date |
get_hrv_data | ์ฌ๋ฐ๋ณ์ด๋ (HRV) | date |
get_activity_hr_zones | ํ๋๋ณ ์ฌ๋ฐ์กด ๋ถํฌ | activity_id |
Wellness
| ๋๊ตฌ | ์ค๋ช | ์ฃผ์ ํ๋ผ๋ฏธํฐ |
|---|---|---|
get_sleep_data | ์๋ฉด ๋ฐ์ดํฐ | date |
get_daily_wellness | ์คํธ๋ ์ค/๋ฐ๋๋ฐฐํฐ๋ฆฌ/SpO2/ํธํก์ | date |
get_weekly_wellness_summary | ์ฃผ๊ฐ ์ฐ๋์ค ํธ๋ ๋ | end_date, weeks (์ต๋ 4) |
Records & Goals
| ๋๊ตฌ | ์ค๋ช | ์ฃผ์ ํ๋ผ๋ฏธํฐ |
|---|---|---|
get_personal_records | ๊ฐ์ธ ๊ธฐ๋ก (1K~๋ง๋ผํค) | ์์ |
get_goals | ํผํธ๋์ค ๋ชฉํ | status (active/completed/all) |
Workout
| ๋๊ตฌ | ์ค๋ช | ์ฃผ์ ํ๋ผ๋ฏธํฐ |
|---|---|---|
create_running_workout | ์ํฌ์์ ์์ฑ ๋ฐ Garmin์ ์ ๋ก๋ (์๊ฐ/๊ฑฐ๋ฆฌ ๊ธฐ๋ฐ, ํ์ด์ค/์ฌ๋ฐ/์ผ์ด๋์ค/ํ์ ํ๊ฒ) | name, steps, description |
get_workouts | ์ ์ฅ๋ ์ํฌ์์ ๋ชฉ๋ก | count (๊ธฐ๋ณธ 20, ์ต๋ 100) |
Gear
| ๋๊ตฌ | ์ค๋ช | ์ฃผ์ ํ๋ผ๋ฏธํฐ |
|---|---|---|
get_running_gear | ๋ฌ๋ํ ๋ชฉ๋ก + ๋์ ๊ฑฐ๋ฆฌ + ๋ง๋ชจ์จ | ์์ |
์ํฌ์์ ์์ฑ ๊ฐ์ด๋
create_running_workout์ผ๋ก ๊ตฌ์กฐํ๋ ๋ฌ๋ ์ํฌ์์์ ์์ฑํ๊ณ Garmin ์์น์ ๋๊ธฐํํ ์ ์์ต๋๋ค.
Step ํ์
| ํ์ | ์ค๋ช |
|---|---|
warmup | ์๋ฐ์ |
interval | ์ธํฐ๋ฒ (๊ณ ๊ฐ๋) |
recovery | ํ๋ณต ์กฐ๊น |
rest | ์์ ํด์ (์์ ์ฌ๊ธฐ) |
cooldown | ์ฟจ๋ค์ด |
repeat | ๋ฐ๋ณต ๊ทธ๋ฃน |
์ข ๋ฃ ์กฐ๊ฑด (Step ๊ธฐ๊ฐ)
| ํ๋ | ํ์ | ์ค๋ช | ์์ |
|---|---|---|---|
duration_seconds | int | ์๊ฐ ๊ธฐ๋ฐ | "duration_seconds": 300 (5๋ถ) |
distance_meters | int | ๊ฑฐ๋ฆฌ ๊ธฐ๋ฐ | "distance_meters": 1000 (1km) |
ํ๋์ ์ํฌ์์์์ ์๊ฐ/๊ฑฐ๋ฆฌ ๊ธฐ๋ฐ step์ ํผํฉ ์ฌ์ฉํ ์ ์์ต๋๋ค.
Target ํ์
| ํ์ | ๊ฐ ํ์ | ์์ |
|---|---|---|
pace | min:sec/km | "min": "4:30", "max": "4:50" |
heart_rate | bpm | "min": 140, "max": 155 |
cadence | spm | "min": 170, "max": 185 |
power | watts | "min": 280, "max": 320 |
์ต์
- ์ํฌ์์ ๋ฉ๋ชจ:
descriptionํ๋ผ๋ฏธํฐ๋ก ์ ์ฒด ์ค๋ช ์ถ๊ฐ - Step ๋ฉ๋ชจ: ๊ฐ step์
"description": "๋ฉ๋ชจ"์ถ๊ฐ - ๋ง์ง๋ง ํ๋ณต ๊ฑด๋๋ฐ๊ธฐ: repeat step์
"skip_last_rest": true์ถ๊ฐ
์์: 4x1km ๊ฑฐ๋ฆฌ ๊ธฐ๋ฐ ์ธํฐ๋ฒ
{
"name": "4x1km Intervals @4:30",
"description": "10K ๋ ์ด์ค ๋๋น VO2max ์ธํฐ๋ฒ",
"steps": [
{
"type": "warmup",
"duration_seconds": 600,
"description": "๊ฐ๋ณ๊ฒ ์กฐ๊น
"
},
{
"type": "repeat",
"count": 4,
"skip_last_rest": true,
"steps": [
{
"type": "interval",
"distance_meters": 1000,
"target": { "type": "pace", "min": "4:20", "max": "4:40" },
"description": "๋ชฉํ ํ์ด์ค ์ ์ง"
},
{
"type": "recovery",
"duration_seconds": 120,
"description": "์ฒ์ฒํ ์กฐ๊น
์ผ๋ก ํ๋ณต"
}
]
},
{
"type": "cooldown",
"duration_seconds": 600,
"description": "๋ง๋ฌด๋ฆฌ ์กฐ๊น
"
}
]
}์์ธ ์ํฌ์์ ์์ฑ ๊ฐ์ด๋๋ AGENTS.md๋ฅผ, ์ ์ฒด ๋๊ตฌ ์์ฒญ/์๋ต ๊ท๊ฒฉ์ TOOL_SPEC.md๋ฅผ ์ฐธ์กฐํ์ธ์.
ํ์ฉ ์์
Claude Desktop์์ ๋ค์๊ณผ ๊ฐ์ด ํ์ฉํ ์ ์์ต๋๋ค:
- "์ด๋ฒ ์ฃผ ๋ฌ๋ ์์ฝํด์ค"
- "์ต๊ทผ 3๊ฐ์ ์ฃผ๊ฐ ๋ณผ๋ฅจ ํธ๋ ๋ ๋ถ์ํด์ค"
- "๋ด VO2max ๊ธฐ์ค์ผ๋ก Jack Daniels VDOT ํ๋ จ ํ์ด์ค ๊ณ์ฐํด์ค"
- "์ต๊ทผ ํ๋๋ค์ ์ฌ๋ฐ์กด ๋ถํฌ๋ฅผ ๋ณด๊ณ 80/20 ๋น์จ์ ์งํค๊ณ ์๋์ง ํ์ธํด์ค"
- "๋ด์ผ 4x1km ์ธํฐ๋ฒ ์ํฌ์์ ๋ง๋ค์ด์ค"
- "๋ด ๋ฌ๋ํ ์ค ๊ต์ฒด ์๊ธฐ๊ฐ ๋ ๊ฒ์ด ์๋์ง ํ์ธํด์ค"
- "์๋ฉด๊ณผ ํ๋ จ ์ค๋น๋์ ์๊ด๊ด๊ณ๋ฅผ ๋ถ์ํด์ค"
- "์ต๊ทผ ํธ๋ ์ผ๋ฌ๋์ ๊ฒฝ์ฌ ๊ตฌ๊ฐ๋ณ ์ฑ๊ณผ๋ฅผ ๋ถ์ํด์ค"
- "ํธ๋ ์ผ๋ฌ๋์์ ๊ฑท๊ธฐ/๋ฌ๋ฆฌ๊ธฐ ๋น์จ์ ํ์ธํด์ค"
์ง์ํ๋ ๋ฌ๋ ํ๋ จ ๋ฐฉ๋ฒ๋ก
| ๋ฐฉ๋ฒ๋ก | ํ์ฉ ๋ฐ์ดํฐ |
|---|---|
| Jack Daniels VDOT | VO2max, PR, ๋ ์ด์ค ์์ธก |
| Norwegian Double Threshold | ์ ์ฐ์ญ์น, ์ฌ๋ฐ์กด |
| 80/20 Training | ์ฌ๋ฐ์กด ๋ถํฌ |
| Hanson's Method | ์ฃผ๊ฐ/์๊ฐ ๋ณผ๋ฅจ, ํ์ด์ค ํธ๋ ๋ |
| Pfitzinger | ์ฃผ๊ฐ ๋ณผ๋ฅจ, ์ฅ๊ฑฐ๋ฆฌ ๋ฐ ๋ถ์ |
| ํธ๋ ์ผ/์ธํธ๋ผ ๋ถ์ | ClimbPro ๊ฒฝ์ฌ ๊ตฌ๊ฐ, ๋ ์จ, RWD, GAP |
๊ฐ๋ฐ
# ์ฝ๋ ์์ ํ ํจํค์ง ์ฌ์ค์น
uv sync --reinstall-package garmin-mcp
# Claude Desktop ์ฌ์์์ผ๋ก MCP ์๋ฒ ๋ฐ์๊ฐ์ธ์ ๋ณด ๋ณดํธ
๋ชจ๋ Garmin API ์๋ต์์ ๋ค์ ๊ฐ์ธ์ ๋ณด ํ๋๊ฐ ์๋์ผ๋ก ์ ๊ฑฐ๋ฉ๋๋ค:
- ์์ ์ ์ ๋ณด:
ownerId,ownerFullName,ownerDisplayName,userId, ํ๋กํ ์ด๋ฏธ์ง URL - ํ๋กํ ID:
userProfilePK,userProfileId,profileId,profileNumber - ์ฌ์ฉ์ ์ ๋ณด:
displayName,fullName,userPro,userRoles - GPS ์ขํ:
startLatitude,startLongitude,endLatitude,endLongitude
์ด ํํฐ๋ง์ src/garmin_mcp/sanitize.py์ strip_pii() ํจ์๋ฅผ ํตํด ์ฌ๊ท์ ์ผ๋ก ์ฒ๋ฆฌ๋ฉ๋๋ค.
๊ธฐ์ ์คํ
- python-garminconnect - Garmin Connect API ํด๋ผ์ด์ธํธ
- FastMCP - MCP ์๋ฒ ํ๋ ์์ํฌ
- uv - Python ํจํค์ง ๋งค๋์
- garth - OAuth ํ ํฐ ๊ด๋ฆฌ
๋ผ์ด์ ์ค
MIT
git clone https://github.com/leewnsdud/garmin-connect-mcp.git
cd garmin-connect-mcp
uv syncBefore it works, you'll need: GARMIN_TOKEN_DIRGARMINTOKENS
No common issues documented yet. If you hit a problem, the repository's GitHub Issues page is the best place to look.