Garmin Connect MCP
Provides read-only access to a user's Garmin Connect data through Garmin's Personal API, including activities, sleep, HRV, Body Battery, stress, steps, heart rate, weight, nutrition, and women's health metrics.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Garmin Connect MCP How did I sleep this week compared to last week?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Garmin Connect MCP
Let Claude, Gemini, ChatGPT or any other MCP client read your own Garmin data (activities, sleep, HRV, Body Battery, stress, steps, heart rate, weight and more) through Garmin's official Connect Personal API.
"How did I sleep this week compared to last week?" · "Summarize my last five runs." · "Which days this month did I miss my step goal?"
It exposes all 26 Personal API endpoints as read-only tools, and you can host it yourself for free on Vercel.
Unofficial community project, not affiliated with or endorsed by Garmin.
Set up on Vercel
1. Create a Garmin API key. In Garmin Connect on the web, open Settings → Personal API Token, select Create Key and copy it. Garmin shows it only once.
2. Generate two secrets: a client secret for the app to store, and a sign-in password that only you type. Run this twice:
openssl rand -base64 323. Deploy and fill in the variables when Vercel asks:
Environment variables
Variable | Value | What it's for |
| The key from step 1 | Reads your Garmin data. Required. |
| First secret from step 2 | The client secret you enter in Claude, Gemini or ChatGPT. |
| Second secret from step 2 | Your sign-in password, typed on your server's sign-in page when you connect an app (step 5). Also lets header-based clients like Claude Code connect directly. |
|
| The client ID you enter in the app. Keep the default. |
| Optional, add later if needed | Extra allowed sign-in callback URLs, comma-separated. Claude's, Gemini's and ChatGPT's are already allowed. |
MCP_OAUTH_CLIENT_SECRET and MCP_AUTH_TOKEN are each optional, but at least one must be set. Without MCP_AUTH_TOKEN, the sign-in page asks for the client secret instead.
Your server is now at https://<your-project>.vercel.app/mcp. Vercel's free Hobby plan is enough. Recommended: under Settings → Environment Variables, recreate the two secrets as Sensitive (their values can't be read back afterwards), then redeploy. The deploy button can't do this for you.
4. Add a custom connector in your app with:
Field | Value |
Server URL |
|
Client ID |
|
Client secret | Your |
Claude (claude.ai and Claude Desktop): Settings → Connectors → Add custom connector. The client ID and secret go under Advanced settings.
Gemini: add a custom MCP connector and fill in Client ID and Client secret. If it also asks for URLs, use
https://<your-project>.vercel.app/oauth/authorize(authorization),https://<your-project>.vercel.app/oauth/token(token) and scopegarmin:read.ChatGPT: create a connector in developer mode (on plans that offer custom connectors), choose OAuth with a user-defined OAuth client, and leave the registration URL empty.
5. Sign in. When you connect, Claude, Gemini or ChatGPT opens a sign-in page from your own server that asks for MCP_AUTH_TOKEN. Paste it and select Allow access. You only do this once per connection.
This is expected. The client ID and secret identify the app; the sign-in page proves the person connecting is you, so a leaked client secret alone isn't enough to read your data. If the page says a redirect URL isn't allowed, add that URL to MCP_OAUTH_REDIRECT_URIS and redeploy.
Claude Code connects with the token directly:
claude mcp add --transport http garmin https://<your-project>.vercel.app/mcp --header "Authorization: Bearer <MCP_AUTH_TOKEN>"Other clients, running locally, and other hosts: see docs/deployment.md.
Related MCP server: Garmin MCP Worker
Tools
All tools are read-only. Dates are YYYY-MM-DD in your local calendar.
Area | Tools |
Profile |
|
Activities |
|
Daily wellness |
|
Sleep |
|
Heart & recovery |
|
Body |
|
Nutrition |
|
Women's health |
|
Each tool's description tells the model what it returns, its units and its limits, so you don't need to learn them.
Good to know
Your own data only, read-only. That's the scope of Garmin's Personal API. It doesn't cover commercial or multi-user apps, and it can't upload workouts.
Keys expire. You choose 30 days to 1 year when creating the key, and Garmin emails you before it expires. Create a new key, update
GARMIN_API_KEYin Vercel, and redeploy.Keep your secrets secret.
MCP_AUTH_TOKENandMCP_OAUTH_CLIENT_SECRETare what protect your data. If one leaks, change it in Vercel and redeploy. Changing either also signs out every OAuth connection.
Development
npm install && npm testnpm run smoke checks every tool against the real API using GARMIN_API_KEY from .env, and prints only response shapes, never your data. Architecture and conventions are in AGENTS.md.
License
MIT © Pedro Kehl
Available Tools
26 toolsgarmin_download_activity_fileDownload original activity file (FIT)ARead-onlyIdempotent
Download the original file recorded by the device for an activity, usually a ZIP containing the .FIT file with every sample (GPS track, heart rate, power, cadence…). When running locally with GARMIN_DOWNLOAD_DIR set, the file is saved there and the path is returned. Otherwise the file is returned inline as a base64 embedded resource, up to 3 MB. For summaries, splits or metrics, prefer garmin_get_activity: it is much smaller.
Inputs: activityId (required).
garmin_get_activityGet activity detailsARead-onlyIdempotent
Get the full detail of one activity: name, type, event type, time zone, device and metadata, the summary (distance m, duration s, speeds m/s, heart rate, power, cadence, calories, training effect, elevation), split/lap summaries, and dive or multisport info when relevant. Get the activityId from garmin_search_activities. Does not include per-second samples; use garmin_download_activity_file for the raw FIT data.
Inputs: activityId (required).
garmin_get_activity_workoutsGet workouts performed in an activityARead-onlyIdempotent
List the structured workouts that were followed during an activity (workout name, sport, steps with targets, creation time). Returns an empty array when the activity was not a structured workout.
Inputs: activityId (required).
garmin_get_body_batteryGet Body Battery (date range)ARead-onlyIdempotent
Get Body Battery (energy level 0–100) per day: charged and drained totals, a time series bodyBatteryValuesArray, feedback events and the activities that impacted it. Maximum range: 31 days. Set chunks to get the lowest and highest values of each part of the day instead of reading the full series.
Inputs: endDate (required), startDate (required), chunks.
garmin_get_daily_summariesGet daily wellness summaries (date range)ARead-onlyIdempotent
Get daily wellness summaries (steps, calories, distance, intensity minutes, heart rate, stress, Body Battery, floors…) for every day in a range, returned as { data: [...] } with one entry per day. Maximum range: 365 days (Garmin documents 1 year). Each day is a large object, so a year of data is very long: prefer a few weeks or months per call unless the whole year is needed. For one day garmin_get_daily_summary is simpler.
Inputs: endDate (required), startDate (required).
garmin_get_daily_summaryGet daily wellness summary (one day)ARead-onlyIdempotent
Get the user's complete wellness summary for one day: total/active/BMR kilocalories, steps and step goal, distance (meters), floors climbed, intensity minutes (moderate/vigorous) and goal, highly active/active/sedentary/sleeping seconds, min/max/resting heart rate, stress averages and durations, Body Battery charged/drained/highest/lowest, SpO2 and respiration. Best single call for "how was my day?". For many days at once use garmin_get_daily_summaries.
Inputs: calendarDate (required).
garmin_get_fitness_ageGet Fitness AgeARead-onlyIdempotent
Get the latest Fitness Age as of a date: chronologicalAge, fitnessAge, achievableFitnessAge, previousFitnessAge, and the components behind it (BMI or body fat, resting heart rate, vigorous activity days/minutes) with improvement targets.
Inputs: calendarDate (required), fullHistoryScan.
garmin_get_heart_rateGet all-day heart rate (one day)ARead-onlyIdempotent
Get heart rate for one day: restingHeartRate, minHeartRate, maxHeartRate, lastSevenDaysAvgRestingHeartRate, abnormal-HR alerts, and heartRateValues, a time series of [timestamp, bpm] rows whose columns are described by heartRateValueDescriptors. The time series is large; for resting HR trends over many days prefer garmin_get_daily_summaries.
Inputs: date (required).
garmin_get_heart_rate_zonesGet heart rate zonesARead-onlyIdempotent
Get the user's heart rate zone configuration, one entry per sport (default, running, cycling…): training method (%max HR, %HRR or %LTHR), zone1Floor..zone5Floor (bpm), and the max, resting and lactate threshold heart rates used.
Inputs: generateDefaults.
garmin_get_hrvGet heart rate variability (one night)ARead-onlyIdempotent
Get overnight HRV for one date: hrvSummary (weeklyAvg, lastNightAvg, lastNight5MinHigh, baseline, status: NONE, POOR, LOW, UNBALANCED, BALANCED or INVALID, feedbackPhrase) and hrvReadings (5-minute HRV values in ms during sleep), plus the sleep window timestamps. Requires a compatible watch worn overnight.
Inputs: calendarDate (required).
garmin_get_hrv_summariesGet HRV daily summaries (date range)ARead-onlyIdempotent
Get one HRV summary per day for a date range (weeklyAvg, lastNightAvg, baseline, status, feedback), without the 5-minute readings. Use for HRV trends and recovery analysis.
Inputs: toDate (required), fromDate (required).
garmin_get_hydrationGet daily hydration (date range)ARead-onlyIdempotent
Get logged water intake per day: calendarDate, valueInML (consumed), goalInML, sweatLossInML (estimated from activities), activityIntakeInML and lastEntryTimestampLocal. Only has data if the user logs hydration in Garmin Connect. Maximum range: 31 days.
Inputs: endDate (required), startDate (required).
garmin_get_menstrual_cycle_dayGet menstrual cycle day viewARead-onlyIdempotent
Women's health: detailed view of one day: the user's daily log (flow, symptoms, mood and other entries), the cycle summary for that day, and, during pregnancy tracking, weekly weight goal ranges and optionally baby movement (kick count) data.
Inputs: calendarDate (required), skipbm, todayDate, includeAnalysis, +1 more.
garmin_get_menstrual_cycle_summaryGet menstrual cycle summaryARead-onlyIdempotent
Women's health: get the menstrual cycle that contains a date: cycle start/end, dayInCycle, currentPhase and days until the next phase, period length, predicted cycle length, fertile window, predicted ovulation date, and pregnancy info (due date, week) when pregnancy tracking is on. Only has data if the user tracks their cycle in Garmin Connect.
Inputs: calendarDate (required).
garmin_get_nutrition_logGet nutrition / food log (one day)ARead-onlyIdempotent
Get the user's food log for one day from Garmin Connect nutrition tracking: daily nutrition goals, totals of calories and nutrients consumed (protein, carbs, fat, fiber…), logged foods with serving sizes, and meal-by-meal details (breakfast, lunch, dinner, snacks). Only has data if the user logs food in Garmin Connect.
Inputs: date (required), mealId, includeGoals, includeMedia, +1 more.
garmin_get_sleepGet sleep (one night)ARead-onlyIdempotent
Get sleep data for one calendar date (the night ending on that morning): sleep start/end timestamps, sleepTimeSeconds, deepSleepSeconds, lightSleepSeconds, remSleepSeconds, awakeSleepSeconds, awakeCount, napTimeSeconds, sleep scores and feedback, average/lowest SpO2, respiration, avgHeartRate and avgSleepStress. Durations are in seconds. For several nights use garmin_get_sleep_summaries.
Inputs: date (required).
garmin_get_sleep_summariesGet sleep summaries (date range)ARead-onlyIdempotent
Get one sleep summary per night for a date range, each with sleep start/end timestamps, sleepTimeSeconds, deepSleepSeconds, lightSleepSeconds, remSleepSeconds, awakeSleepSeconds, awakeCount, napTimeSeconds, sleep scores and feedback, average/lowest SpO2, respiration, avgHeartRate and avgSleepStress. Durations are in seconds. Maximum range: 31 days per request.
Inputs: endDate (required), startDate (required).
garmin_get_spo2Get blood oxygen / SpO2 (one day)ARead-onlyIdempotent
Get Pulse Ox (SpO2, %) for one day: averageSpO2, lowestSpO2, latestSpO2, lastSevenDaysAvgSpO2, avgSleepSpO2, single readings and hourly averages, plus altitude acclimation data. Requires a watch with Pulse Ox enabled.
Inputs: calendarDate (required).
garmin_get_stepsGet daily steps (date range)ARead-onlyIdempotent
Get daily step totals for a date range: one entry per day with calendarDate, totalSteps, totalDistance (meters) and stepGoal. Maximum range: 28 days per request; split longer periods, or use garmin_get_daily_summaries (up to 365 days), which also includes steps.
Inputs: endDate (required), startDate (required).
garmin_get_stressGet daily stress (date range)ARead-onlyIdempotent
Get daily stress statistics for a date range: one entry per day with calendarDate and a values map containing overallStressLevel (0–100) and restStressDuration, lowStressDuration, mediumStressDuration, highStressDuration (seconds). Stress is derived from heart rate variability. Maximum range: 28 days per request.
Inputs: endDate (required), startDate (required).
garmin_get_user_settingsGet user profile settingsARead-onlyIdempotent
Get the user's Garmin Connect settings: displayName, timeZone, preferredLocale, measurementSystem (metric/statute), first day of week, date/time/number formats, heart rate and power display formats, hydration and glucose units, golf units, wheelchair mode and preferred training days. Call this first to learn the user's time zone (so you know what "today" is) and preferred units.
Inputs: no named parameters.
garmin_get_weight_entriesGet all weight entries (date range)ARead-onlyIdempotent
Get every individual weigh-in in a date range (from Index scales, other smart scales or manual entries): date, timestampGMT, weight, bmi, bodyFat, bodyWater, boneMass, muscleMass, visceralFat, metabolicAge, physiqueRating and weightDelta (change since the previous weigh-in); weight, boneMass and muscleMass are in GRAMS (75000 = 75 kg); bodyFat and bodyWater are percentages; timestampGMT and date are epoch milliseconds. For one value per day plus the range average use garmin_get_weight_summary.
Inputs: endDate (required), startDate (required).
garmin_get_weight_goalsGet weight goals (date range)ARead-onlyIdempotent
Get the user's weight goals in effect during a date range: userGoals (goalValue = target weight in GRAMS, category such as MANUAL or PREGNANCY, tracking period, start/end dates) and userGoalRanges (low/high target ranges, also in grams).
Inputs: endDate (required), startDate (required).
garmin_get_weight_summaryGet weight summary (date range)ARead-onlyIdempotent
Get a weight summary for a date range: dailyWeightSummaries (latest weigh-in per day, or all of them with includeAll), totalAverage for the range, and the closest weigh-ins before (previousDateWeight) and after (nextDateWeight) the range; weight, boneMass and muscleMass are in GRAMS (75000 = 75 kg); bodyFat and bodyWater are percentages; timestampGMT and date are epoch milliseconds.
Inputs: endDate (required), startDate (required), includeAll.
garmin_get_wellness_activitiesGet wellness activity summaries (date range)ARead-onlyIdempotent
Get wellness activities (e.g. breathwork, meditation, relaxation and other non-fitness sessions recorded on the device) for a date range: name, type, notes, start/end timestamps, summary values and time in zones. Both dates are inclusive. Fitness activities such as runs or rides are not here; use garmin_search_activities for those.
Inputs: fromDate (required), untilDate (required).
garmin_search_activitiesSearch activitiesARead-onlyIdempotent
Search and list the user's recorded activities (runs, rides, swims, strength sessions, hikes, dives…), newest first by default. Returns an array of activity summaries with: activityId, activityName, description, startTimeLocal/startTimeGMT, activityType, eventType, distance (meters), duration / movingDuration / elapsedDuration (seconds), elevationGain/Loss (meters), averageSpeed/maxSpeed (m/s), calories, averageHR/maxHR (bpm), cadence, power, training effect and more. Use this first to find activity IDs, then call garmin_get_activity for splits and full detail, or garmin_download_activity_file for the original FIT file. All filters are optional and combined with AND. Paginate with start and limit.
Inputs: limit, start, search, sortBy, +28 more.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 26 tools
Most tools are clearly separated by metric and resource, with explicit distinctions between singular/range variants and summary/detail variants. There is some intentional overlap between broad daily-summary tools and metric-specific tools, but the descriptions guide selection well.
All tools use a consistent snake_case `garmin_` prefix followed by a clear action such as `get`, `search`, or `download`. The verb-noun pattern is predictable throughout, including range/detail variants.
The set is slightly large at 26 tools, but the Garmin health/fitness domain is broad and most tools cover distinct metrics or lifecycle variants. It is heavy but reasonably scoped rather than bloated.
The read-only surface is extensive, covering sleep, activities, daily wellness, heart rate, HRV, body battery, weight, nutrition, and menstrual health. Some adjacent areas such as training status, VO2 max, devices, or workouts-as-a-list are missing, but core health-data retrieval is well covered.
Maintenance
Related MCP Connectors
Garmin data in Claude: 135 tools — activities, sleep, HRV, training, workouts. Free, open source.
Garmin data in Claude & ChatGPT via the Garmin Health API. OAuth sign-in, no password sharing.
Pace is a remote MCP server that exposes wearable and fitness data to Claude via the Model Context Protocol. It connects to Garmin, Oura, Whoop, Polar, Fitbit and 20+ devices and provides 15 tools for querying sleep, activity, recovery, and training data. Hosted on Google Cloud Run, OAuth 2.1 authentication, Streamable HTTP transport. Instructions: First you need to create an account at: https://pacetraining.co and connect your wearables. After that you can connect the remote Server via Custom Connector in Claude and OAuth 2.1 Flow startet.
Multi-tenant hosted MCP server for Oura Ring — 21 read-only tools, OAuth per user.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceA read-only remote MCP server that lets Claude query your own Garmin Connect data — sleep, HRV, Body Battery, stress, heart rate, and activities — through Garmin's official Personal API. It runs on Cloudflare Workers with OAuth sign-in, so it works from both the Claude mobile app and desktop.MIT
- AlicenseNot gradedqualityNot gradedmaintenanceA read-only remote MCP server that connects Claude to your own Garmin Connect account through Garmin's official Personal API, running on Cloudflare Workers with OAuth sign-in so it also works in the Claude mobile app. It lets you ask natural-language questions about sleep, HRV, resting heart rate, Body Battery, stress, daily summaries and activities across date ranges.MIT
- AlicenseNot gradedqualityNot gradedmaintenanceA read-only remote MCP server that lets Claude query your own Garmin Connect wellness and activity data — sleep, HRV, Body Battery, stress, heart rate, daily summaries, and workout details — through Garmin's official Personal API, with OAuth sign-in so it works from mobile and desktop clients.MIT
- AlicenseNot gradedqualityNot gradedmaintenanceA read-only remote MCP server that lets Claude query your own Garmin Connect data — sleep, HRV, Body Battery, stress, heart rate and activities — through Garmin's official Personal API, with OAuth sign-in so it works in the mobile app as well as on desktop.864 npmMIT