garmin-connect-mcp
Provides tools for interacting with Garmin Connect, enabling AI agents to read activity and wellness data (activities, sleep, steps, heart rate, weight), manage workout templates, and schedule workouts in the Garmin Calendar.
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-mcpShow my last 5 activities and sleep summary from this 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
A standalone Garmin Connect MCP server for AI agents. Connect any client that supports local MCP stdio to read activity and wellness data, create structured workouts, and schedule training in Garmin Calendar.
中文说明 · Client setup · Migration · Verification report · Write safety and recovery
No model-provider API key or agent framework is required by this server. Garmin access uses your own account. An optional agent skill provides usage guidance; it is not required to expose tools.
Install from source
Use Node.js 22 for development (runtime requires Node.js 20+), npm, and GitHub CLI:
gh repo clone xcbbc21/garmin-connect-mcp
cd garmin-connect-mcp
npm ciThe install builds lib/. After a source update, run npm ci again. npm run build always clears this project's generated output before compiling.
This fork is distributed from GitHub only. The unscoped npm name is owned by another project. Do not install it to obtain this repository. The local manifest uses private: true; there is no npm release for this fork.
Related MCP server: garmin-mcp
First login
In the source directory, set your account email and choose your Garmin region:
export GARMIN_USERNAME='your@email.com'
node lib/auth-cli.js serve --account personal-codex --region global --openUse --region cn for China. The command requires an explicit account alias and region. The same account alias, email and region must be used in the MCP configuration.
Your system browser opens a short-lived local login page. Enter credentials only in the Garmin sign-in form, then confirm the returned account identity. The local page embeds Garmin's sign-in page; its outer address is a loopback URL, not a Garmin URL. If the form is absent, do not enter credentials.
The default macOS session path, when no configuration-root override is set, is:
~/.config/garmin-connect-mcp/accounts/personal-codex.session.json.
XDG_CONFIG_HOME, LOCALAPPDATA and APPDATA can override the root. Use the destination selected by the CLI when configuring a custom path.
For a second concurrently running client, initialize a separate alias, for example personal-claude. Do not copy or concurrently share session files: refresh tokens can rotate.
garmin-connect-auth remains the authentication executable name. In a source checkout, call it through node lib/auth-cli.js; global installation is unnecessary. Diagnostic login, login --browser and canary commands are retained. Normal serve does not require Playwright; older browser diagnostics use the optional driver.
Connect a client
Every client runs the same program:
command: the absolute path to your Node executable (
command -v nodeon macOS/Linux);argument: the absolute path to this checkout's
lib/mcp.js;environment:
GARMIN_USERNAME,GARMIN_REGION,GARMIN_ACCOUNTand your session-file path.
See client setup for Codex, Claude Desktop, Claude Code, Cursor, Windsurf, WorkBuddy and ZCode examples. These are configuration examples; protocol tests do not establish end-to-end validation of every desktop application.
If a client supports MCP URL elicitation, missing/expired sessions can prompt the same browser login. Otherwise run the independent login command above and retry. Authentication completion never automatically replays a write.
Tools
Capability | MCP tools |
Activities and wellness |
|
Account and templates |
|
Running guidance |
|
Create a template |
|
Calendar scheduling |
|
Activity export |
|
Calendar read |
|
Write inspection and recovery |
|
There are 18 tools. Workout-library templates describe what to do; Calendar entries describe when to do it. The guidance tool offers training-method knowledge and athlete-intake checks; it does not independently generate and execute a complete training plan.
Workout creation and Calendar writes use two calls: first preview, then repeat the identical request with confirmed: true and the returned confirmationId after the user approves. IDs expire after ten minutes and cannot be reused. A confirmationId is <operationId>:<previewRevision>: because the revision and its deadline are stored with the operation, an unexpired handle still resolves after a restart, and re-previewing invalidates every earlier handle. The write journal is durable — Calendar results are recorded on disk and survive a restart. See write safety and recovery.
All five write tools — create_garmin_workout, schedule_garmin_workout, batch_schedule_garmin_workouts, create_and_schedule_garmin_workout and unschedule_garmin_workout — also accept an optional idempotencyKey (1–128 characters from A-Z a-z 0-9 . _ : -). It is a request label, not a permission token: reusing the same key with the same request returns the recorded receipt instead of writing again, and a different key never bypasses an in-flight or unknown write. confirmationId and idempotencyKey are never interchangeable.
Calendar writes are recorded under an account-scoped local directory, GARMIN_STATE_DIR (absolute, local, private; default <platform config root>/garmin-connect-mcp/state). It is independent of your login alias, so two aliases for the same account share one recovery record while session files stay separate. Back it up; deleting it destroys the records that prevent duplicate scheduling.
Five runs next week
Ask your agent:
Find the existing easy, threshold and long-run templates. Preview five runs next Monday, Tuesday, Thursday, Saturday and Sunday in Asia/Shanghai. Show dates and workouts before writing. Leave the other days empty.
The agent resolves real workout IDs and dates first. Example batch preview (replace these example IDs and dates):
{
"schedules": [
{ "workoutId": "123", "date": "2026-09-14" },
{ "workoutId": "456", "date": "2026-09-15" },
{ "workoutId": "123", "date": "2026-09-17" },
{ "workoutId": "123", "date": "2026-09-19" },
{ "workoutId": "789", "date": "2026-09-20" }
],
"timezone": "Asia/Shanghai"
}After approval, send the same request to batch_schedule_garmin_workouts, adding confirmed: true and the returned ID. A batch accepts 1–100 entries, so it can span multiple weeks.
Dates are local
YYYY-MM-DDvalues; the timezone defaults to the server host if omitted. Past or impossible dates and invalid IANA timezones are rejected.Reusing a template on different dates is supported. Duplicate workout/date pairs within one batch are rejected before anything is sent.
Within one shared write journal, a template/date pair is not written twice: a repeated request is either skipped because a Calendar read shows the entry already there, or blocked because an earlier write for the same pair has an unknown outcome. This holds across a new preview, a different
idempotencyKey, a service restart and concurrent callers. The guarantee is local — it covers the machines that shareGARMIN_STATE_DIR. Garmin exposes no server-side idempotency, so a second state directory, another device, or a hand-edited Calendar is outside it, andskippedreports what the read returned rather than proving that no other entry exists.Rest days are omitted; a workout's internal recovery/rest step is still valid.
Preview checks existing template IDs. Each confirmed entry is committed separately and reports its own
status. A batch continues after an individual failure:successCountcountssucceeded+skipped, and the legacyfailureCountmeans "not confirmed complete", not "definitely failed" — branch on per-entrystatusanddefiniteFailureCount, never onfailureCount.A timeout is reported as
status: "unknown"together with a durableoperationId, not as a plain failure. It is never re-sent. Check Garmin Calendar, then schedule a different date or template. Creation followed by failed scheduling reports the created workout ID when available.Cancellation needs the
workoutScheduleIdfrom the scheduling result, not the template ID. If Garmin does not return that ID, do not invent one.create_garmin_workout,create_and_schedule_garmin_workoutandunschedule_garmin_workoutare journal-backed and accept the same optionalidempotencyKey. A create-and-schedule records both phases, so a crash after the template exists is recoverable without creating a second template, and a cancellation is logged rather than repeated. Read the current Calendar withget_garmin_calendar, then inspect and recover withget_garmin_write_operation,reconcile_garmin_write_operationandresume_garmin_write_operation. See write safety and recovery.
The pinned garmin-connect@1.6.2 does not export schedule/cancel helpers. The existing adapter uses authenticated POST /workout-service/schedule/{workoutId} and DELETE /workout-service/schedule/{workoutScheduleId} requests. These are unofficial endpoints; mocked protocol/transport tests are not proof of current live Garmin acceptance or watch synchronization.
Recovering an uncertain write
The example above stops at the per-entry receipts. If one entry comes back unknown, the rest of the chain is:
Read the entries. The batch result reports
status,actionandevidenceper entry.succeededcarries a write receipt;skippedmeans the Calendar read already showed that entry.Query the operation.
get_garmin_write_operationtakes exactly one ofoperationIdoridempotencyKey, or neither to page through the newest operations (limitdefaults to 20, maximum 100). It returns the durable record — which steps aresucceeded,unknown,preparedornot_attempted, pluscanResume,manualReviewRequiredandnextAction.Reconcile.
reconcile_garmin_write_operationre-reads Garmin inside a fixed budget (at most 3 reads, 20 seconds) and reports what it observed. It never rewritesstatus: anobserved_presentresult satisfies the desired state, but the original step staysunknown, because the observation cannot prove this request caused the entry. An empty Calendar read proves nothing, so it never authorizes an automatic re-post.Resume only the safe steps.
resume_garmin_write_operationpreviews first, exactly like any other write. It arms only steps that were never dispatched; a step whose outcome is unknown is listed asblockedand is never re-sent. Confirm the preview with the returned ID to commit the remaining steps.
Never delete the state directory, and never re-issue the same write to "clear" an unknown: neither removes the record, and the second write may duplicate the entry.
Other examples
“Show my last five runs.”
“Compare the last seven complete days of sleep and resting heart rate.”
“Preview a 3×8-minute threshold workout; wait for my approval before creating it.”
“Download the FIT file for activity 123456789.” Set
GARMIN_FIT_DOWNLOAD_DIRfirst.
Configuration and troubleshooting
See .env.example for all environment variables. MCP reads .env in its launch working directory; desktop clients should use explicit env fields. The login command example uses shell environment variables.
Symptom | Check |
Client cannot start Node | Use the absolute executable and |
Username missing | Set |
Session missing or expired | Login with the same alias/region and exact destination. |
Session permissions rejected | Use a private, locally owned destination; keep the runtime's owner-only permissions. |
Confirmation expired or changed | Request a new preview and obtain approval again. |
Schedule blocked, | An earlier write for that template/date is unresolved. Do not retry the write: read Garmin Calendar with |
Recorded operation not found |
|
New writes refused / state unavailable | Verify |
FIT export unavailable | Choose a trusted absolute |
Activity detail defaults to compact. Full detail may include precise routes/locations. Health estimates are not medical diagnoses. Keep credentials, sessions and personal data outside Git.
Optional skill and programmatic API
skills/garmin-connect-mcp/SKILL.md is neutral usage guidance for clients that support agent skills. Skill installation is client-specific and never a prerequisite for MCP. See the skill.
The package root exports createMcpServer, GarminClient, GarminToolService, resolveConfig, resolveAccountAlias, logging helpers and their public types. Importing the package neither loads dotenv nor starts a service. Construct the client as new GarminClient(config, { logger }); the logger implements debug/info/warn/error(message: string).
Development and verification
npm ci
npm run lint
npm test -- --runInBand
npm run test:coverage
npm run pack:smoke
npm run test:distributionTests build first. Lint never rewrites files. Packaging audits file contents and obsolete dependencies. CI checks Node 20/22 on Linux and platform behavior on macOS/Windows.
npm run test:integration is an explicitly invoked, read-only live check using the same client/session flow. Configure an authenticated account first. It is not run by CI; GARMIN_INTEGRATION_VERBOSE=true prints normalized personal data and should only be used deliberately. Setting GARMIN_CALENDAR_PROBE_RANGE=YYYY-MM-DD..YYYY-MM-DD additionally reads that one calendar range, which is the only check that exercises the calendar read; without it the calendar probe reports skipped, which is not a pass. Add GARMIN_INTEGRATION_VERBOSE=true when running it, otherwise the reported fields are not printed and the run answers reachability only. The outcome is passed, failed, refused (region not queryable — nothing was sent) or skipped; the last three are not passes, and failed and refused exit non-zero. See calendar API verification.
For this refactor's actual results and limits, see verification. License and upstream attribution are retained in LICENSE and NOTICE.
This server cannot be deployed
Maintenance
Related MCP Connectors
Garmin data in Claude & ChatGPT via the Garmin Health API. OAuth sign-in, no password sharing.
Connect your health, fitness, nutrition, sleep, and wearable data to your AI assistant.
- freddyOAuthcoach.freddy
Connect your wearables, rings and training apps, then ask your AI about your own health data.
List, fetch, create, edit (replace), delete and schedule structured workouts on Garmin Connect (runn
Related MCP Servers
- FlicenseNot gradedqualityBmaintenanceEnables AI assistants to access and query Garmin Connect health and fitness data, including sleep, HRV, training load, and activities, with an optional coaching plugin for personalized training plans.6 npm4-
- FlicenseNot gradedqualityCmaintenanceExposes Garmin Connect data and workout management to AI agents, supporting tools, resources, and prompts for health data, workout creation, and coaching workflows.1-
- AlicenseNot gradedqualityDmaintenanceEnables AI agents to access Garmin Connect activities, workouts, and workout templates for querying and creating workout plans.5MIT
- AlicenseNot gradedqualityDmaintenanceEnables AI assistants to query live Garmin Connect health and fitness data, including daily metrics, activities, sleep analysis, and trends via natural language.MIT