DeepSeek Harness plugin

garmin-connect-plugin-for-dsh

A DeepSeek Harness plugin that exposes Garmin Connect fitness and health data as AI-callable tools

Jump to install

Source facts

Repository
Likenttt/garmin-connect-plugin-for-dsh
Latest update
Aug 21, 2026
Category
Tools & Capabilities
GitHub stars
10
Format
plugin
Catalog evidence
Upstream dsh.bundle evidence
Evidence path
package.json#dsh.bundle
Checked against
0.1.0-rc.8
Upstream check date
2026-08-20

This evidence comes from the upstream catalog. This site has not installed, run, or security-reviewed the plugin.

Install

Start with a prompt that asks an agent to review the GitHub repository and source. Switch to the command if you want to install it yourself.

Copy this prompt into DSH, Codex, or another agent and ask it to review the GitHub repository and source first.

Do not install or run any commands yet. Read this plugin's GitHub repository, README, and relevant source code. Then answer the questions below clearly and directly so I can decide whether it fits my needs:

1. What is this plugin, and what problem does it solve?
2. Who is it for, and what are its typical use cases?
3. How is it used after installation? Include one minimal example.
4. What known limitations or privacy, security, compatibility, or maintenance risks does it have?
5. Give a clear recommendation: recommend, conditionally recommend, or do not recommend, with reasons.

Distinguish statements documented by the repository, inferences from source code, and unknowns. If evidence is insufficient, say so explicitly. Do not guess or simply repeat the README.

GitHub: https://github.com/Likenttt/garmin-connect-plugin-for-dsh
Plugin: garmin-connect-plugin-for-dsh
Author: Likenttt

Check the source files

Read the README and other files from this plugin directory before installing.

File explorer4 files
README.mdSource · read only
README language

dsh-plugin-garmin-connect

> A DeepSeek Harness plugin that brings your Garmin fitness & health data into the AI agent loop.

![npm version](https://www.npmjs.com/package/dsh-plugin-garmin-connect) ![npm downloads](https://www.npmjs.com/package/dsh-plugin-garmin-connect) ![CI](https://github.com/Likenttt/garmin-connect-plugin-for-dsh/actions/workflows/ci.yml) ![Test Report](TEST_REPORT.md) ![Node.js](https://nodejs.org) ![License: MIT](LICENSE)

English | 简体中文 | [Test Report](TEST_REPORT.md) | [Changelog](CHANGELOG.md)

> [!WARNING] > 0.1.5 status: Garmin two-step verification is unfinished and is not a > supported release capability. The browser authentication commands documented > below are developer previews for local testing only; do not depend on them for > production access or session recovery.

---

More Apps

---

What It Does

This plugin connects DeepSeek Harness to Garmin Connect, exposing your wearable data as AI-callable tools. Once installed, the DeepSeek agent can automatically query your activities, sleep, steps, and heart rate to provide personalized fitness insights — all through natural language.

Registered Tools

The plugin registers 10 tools. Eight return Garmin data without writing; download_garmin_activity_fit writes one local file on the MCP/dsh host, and create_garmin_workout changes the user's Garmin workout library.

ToolDescriptionExample Args
get_garmin_activitiesFetch recent activities (runs, rides, swims…) with compact or full detail{"limit": 5, "detail": "compact"}
get_garmin_sleepSleep score, duration, and stage breakdown for a date or range{"startDate": "2023-10-01", "endDate": "2023-10-02"}
get_garmin_stepsStep totals for a date or range; goal/distance appear only when Garmin supplies them{"startDate": "2023-10-01"}
get_garmin_heart_rateResting, max, and min heart rate for a date or range{"startDate": "2023-10-01", "endDate": "2023-10-02"}
get_garmin_weightBody composition (weight, BMI, body fat %, muscle mass, etc.) for a date or range{"startDate": "2023-10-01"}
get_garmin_workoutsWorkout templates from your Garmin workout library (not calendar scheduling){"limit": 10, "offset": 0}
get_garmin_profileUser profile summary{} or omit
get_running_skill_adviceExplain 8 workout types and 4 training philosophies, or collect the mandatory intake for personalized coaching{"mode": "explain", "query": "Daniels", "language": "en"}
download_garmin_activity_fitDownload an activity's original archive and safely extract its single FIT file to the account directory under the configured host parent{"activityId": 123456789}
create_garmin_workoutPreview a structured workout; create it only after explicit confirmation{"name": "Threshold 3×8min", "steps": [...]}

Workout creation is a two-call flow. The preview response includes a one-time confirmationId; after the user approves the unchanged preview, call the tool again with the same definition, confirmed: true, and that confirmationId. An ID expires after 10 minutes and cannot be reused.

Personalized running coaching

get_running_skill_advice deliberately separates explanation from planning:

  • mode: "explain" explains a workout type or training philosophy. It does not

invent an athlete-specific schedule.

  • mode: "personalized" is required for any recommendation or plan. Before it

returns planning material, the tool requires answers for all six intake areas below. Missing answers are returned as focused questions; Garmin activity data is not fetched and a schedule must not be generated yet.

Intake fieldWhat the assistant must ask
goalTarget distance/event, future ISO YYYY-MM-DD date, and completion or ideal/minimum time goal
currentPerformance + performanceBasisA representative race/time trial from the past two years, result, non-future date, effort/conditions, or an explicit no_recent_benchmark
trainingBackgroundRunning history and recent 4–8 week volume, frequency, long run, quality work, and interruptions
availabilityAvailable days/time, fixed rest and long-run days, terrain/facility limits, strength-training time, and whether double days are possible
healthConstraints + hasWarningSymptomsCurrent/past-year injury, pain, relevant disease/medication, sleep and recovery, plus an explicit warning-symptom boolean
trainingPreference + preference detailssteady, hard_easy, or mixed, plus maxQualitySessionsPerWeek (0–7) and intensityGuidancePreference (pace, heart_rate, rpe, or mixed)

If hasWarningSymptoms is true—for example current chest discomfort, unusual breathlessness with mild activity, fainting/dizziness, or abnormal palpitations—the tool returns a safety stop without workout material or Garmin activity access. It advises medical clearance and does not diagnose. If performanceBasis is no_recent_benchmark, planning material instructs the assistant to begin with easy base work or a low-risk benchmark instead of inventing precise threshold or interval paces.

The compact philosophy layer contains:

  • Hansons — frequent, more evenly distributed mileage, pace discipline, and

cumulative fatigue; its 16-mile long run is not a standalone prescription.

  • Jack Daniels — derive VDOT and E/M/T/I/R intensity from current, recent

performance, never from the goal time.

  • Norwegian threshold — borrow controlled, non-exhaustive threshold work and

hard/easy separation; double-threshold days are not prescribed by default.

  • Polarized training — keep most work genuinely easy and a small amount

clearly hard; 80/20 is a direction rather than an exact quota.

Recent Garmin runs may supplement this intake but never replace the athlete's answers. The method notes and evidence boundaries are summarized in the training-method research note. Each philosophy and workout-card output labels its statements as system_principle, research_evidence, or application_inference so a method definition or coaching inference is not misrepresented as comparative proof.

---

Quick Start

1. Install this plugin — from the npm registry (recommended)

npx --legacy-peer-deps=false @deepseek-ai/dsh plugin --profile web add dsh-plugin-garmin-connect

This single command installs the dependency and activates the plugin layer — the first run automatically initializes the web profile. You only need pnpm on your PATH:

npm install -g pnpm

> --legacy-peer-deps=false makes npm resolve peer dependencies normally. If your npm config has legacy-peer-deps=true (it skips peer packages), dsh would fail to boot with ERR_MODULE_NOT_FOUND: Cannot find package '@deepseek-ai/cordis-plugin-group'. On machines without that setting the flag is a harmless no-op.

Verify the plugin layer is composed without booting:

npx --legacy-peer-deps=false @deepseek-ai/dsh --profile web --dump-config | grep -A 2 garmin-connect

Other install sources:

# Local checkout (development)
cd garmin-connect-plugin-for-dsh && npm install
npx --legacy-peer-deps=false @deepseek-ai/dsh plugin --profile web add .

# GitHub source install
npx --legacy-peer-deps=false @deepseek-ai/dsh plugin --profile web add github:<owner>/<repo>

2. Install the Harness CLI (if you haven't already)

npx --legacy-peer-deps=false @deepseek-ai/dsh web

The web UI starts at http://127.0.0.1:3080 by default. If you launch Harness via npx, keep using the same prefix for the commands below (npx --legacy-peer-deps=false @deepseek-ai/dsh …); if you have dsh installed globally, you can drop the npx @deepseek-ai/ prefix.

3. Configure Credentials

The plugin does not persist credentials itself. Use environment variables (or a secret store provided by your launcher) and keep .env out of version control.

# Source checkout only: copy the bundled template
cp .env.example .env

# Edit .env and fill in your Garmin credentials

For a registry installation, create .env directly in the directory where you run dsh (your workspace root), then add the variables from the table below; the package's template is inside the installed dependency rather than your current directory. The plugin loads the workspace .env automatically.

| Variable | Required | Description | |---|---|---| | GARMIN_USERNAME | ✅ | Your Garmin account email | | GARMIN_PASSWORD | ✅* | Legacy direct-login password; do not use this for the interactive MFA setup below | | GARMIN_SESSION_TOKEN | ✅* | Inline pre-authenticated token (supported, but the session file is safer) | | GARMIN_SESSION_TOKEN_FILE | ✅* | Path to the owner-only DI v2 (or compatible legacy OAuth) session file generated by the local auth command | | GARMIN_REGION | ❌ | global (default) or cn for Garmin China | | GARMIN_FIT_DOWNLOAD_DIR | FIT only | User-selected host parent directory for FIT exports; no default. The generated account directory includes GARMIN_REGION | | GARMIN_CACHE_TTL | ❌ | Cache duration in seconds (default: 300) | | GARMIN_REQUEST_TIMEOUT_MS | ❌ | Garmin request timeout in milliseconds (default: 15000) | | GARMIN_LOG_LEVEL | ❌ | debug \| info \| warn \| error | | GARMIN_ACTIVITY_DETAIL | ❌ | compact (default) or full (expanded fitness plus precise route/location fields; credentials and account/social identifiers are filtered) |

> \* You need one of GARMIN_PASSWORD, GARMIN_SESSION_TOKEN, or > GARMIN_SESSION_TOKEN_FILE. A protected file is safer than an inline token, > especially when isolating multiple processes; this does not make the unfinished > MFA bootstrap a supported workflow. If more than one is configured, the inline token takes > precedence over the file, and a valid session takes precedence over password > login. > > ⚠️ If your password contains # or other special characters, wrap it in double quotes — otherwise # and everything after it will be treated as a comment: > `` > GARMIN_PASSWORD="my#secret!pass" > ` > > GARMIN_SESSION_TOKEN and the contents of GARMIN_SESSION_TOKEN_FILE` are as > sensitive as a password. Token export is intentionally not AI-callable; never > paste a token into an AI conversation.

#### Two-step verification — unfinished developer preview

The browser setup below is retained for development and diagnosis, but it is not a supported 0.1.5 authentication path. If you choose to test it, run it yourself in a trusted local terminal from this source checkout and select the account's region explicitly:

# Garmin International
npm run auth:setup -- --browser --account personal --region global

# Garmin China
npm run auth:setup -- --browser --account personal --region cn

auth:setup is the source-checkout npm-script alias. The stable executable name for an installed package is garmin-connect-auth, so Codex, Claude Code, and other local clients can all point users to the same bootstrap command:

garmin-connect-auth --help
garmin-connect-auth login --browser --account personal --region global
garmin-connect-auth login --browser --account personal --region cn

Direct invocation requires an installation that places npm executables on PATH, normally a global install; a nested dsh dependency or an ordinary local dependency does not do that. Install the published executable globally, or use the package directly with npx:

npm install -g dsh-plugin-garmin-connect@0.1.5
npx -y --package dsh-plugin-garmin-connect@0.1.5 \
  garmin-connect-auth login --browser --account personal --region global

For a source checkout, npm install -g . or npm run auth:setup -- --browser ... remains available. In every form, the command opens an isolated, visible system Google Chrome context. Enter the email, password, MFA code, and any CAPTCHA only on Garmin's page; the CLI does not read those form values, and none may be supplied through flags, environment variables, MCP tool arguments, or model input.

After the browser closes, the CLI exchanges the short-lived service ticket and probes Garmin's region-bound DI profile endpoint. The local terminal then shows a sanitized Garmin profile label beside the requested account alias and configured username. Type exactly yes only if they identify the intended account. Any other answer cancels the write. A successful confirmation writes an owner-only DI v2 session and prints its path; Codex, Claude Code, the model, and other agents must never read or copy the session contents or credentials.

The legacy terminal flow without --browser remains available for compatibility. It reads hidden password/MFA input in the local terminal, but it cannot reliably complete browser-only challenges such as CAPTCHA and is not a supported two-step-verification solution.

For troubleshooting the same browser/DI path without creating credentials, this checkout also includes a deliberately non-persisting diagnostic:

# Choose the account's region explicitly.
npm run auth:canary -- --region global
npm run auth:canary -- --region cn

The experimental canary opens an isolated, visible system Google Chrome context. Enter email, password, MFA, or CAPTCHA only on Garmin's page. The CLI does not read those form values; it captures one short-lived service ticket, immediately closes the temporary browser, then performs one region-bound DI token exchange and verifies the profile API. It does not save cookies, tokens, screenshots, traces, video, HAR, or a session file. A passing canary provides partial diagnostic evidence only; 0.1.5 does not provide a supported browser-MFA session-creation workflow.

The canary requires system Google Chrome plus the optional playwright-core driver installed by a normal dependency install. If dependencies were installed with --omit=optional, the canary is unavailable; normal login, dsh, and MCP operation remain unaffected.

The unfinished setup command is designed to save only a DI v2 session after browser login and explicit profile confirmation, then print its path. On POSIX it uses owner-only mode 0600; on Windows it uses the current user's config directory but does not yet validate Windows ACLs. It does not save the password or MFA code. Configure the runtime with the printed path, then omit GARMIN_PASSWORD:

GARMIN_USERNAME=your-email@example.com
GARMIN_REGION=global
GARMIN_SESSION_TOKEN_FILE=/absolute/path/to/personal.session.json
GARMIN_FIT_DOWNLOAD_DIR=/absolute/path/to/garmin-fit-parent

DI v2 files bind the normalized username, region, and probed Garmin profile via one-way hashes, including profileIdHash; they do not duplicate the plaintext email in the binding. The runtime rejects username, region, or profile mismatch before publishing refreshed credentials. It refreshes access tokens before expiry, persists a rotated refresh token before using the new session, and may retry an authentication failure at most once for an idempotent GET. Workout and other write requests are never replayed automatically.

For backward compatibility, legacy session files containing only the two oauth1 and oauth2 fields are still accepted. They have no profile binding; the intended replacement is a validated DI v2 session with the mismatch guard, but the unfinished browser command is not yet a supported way to generate one. On POSIX, a legacy file must still pass the current owner-only file-permission check (normally mode 0600).

On POSIX, the default account directory is created with owner-only permissions. To choose a different session file, add --output /absolute/private/path/personal.session.json. On POSIX systems, an existing parent directory must grant no permissions to group or other users (normally mode 0700); a missing parent is created owner-only. The command refuses an unsafe parent instead of weakening it.

This MFA bootstrap uses Garmin's private SSO/DI flow and is unfinished. On 2026-08-21, a real China-region browser login produced a short-lived service ticket and the DI exchange/profile probe was verified separately. The current browser interception can leave the redirected Garmin page at ERR_BLOCKED_BY_CLIENT, and the complete capture → exchange → confirmed session write → dsh/MCP restart/refresh path has not been revalidated end to end. The Global-region browser path is also unverified. These commands remain a developer preview and are not part of the supported 0.1.5 feature set.

#### Multiple accounts: one isolated process per account

The supported runtime model is one account per process and one independently initialized session per process. Give each dsh, Codex, Claude Code, or other MCP process its own GARMIN_USERNAME, GARMIN_REGION, and GARMIN_SESSION_TOKEN_FILE. The unfinished browser bootstrap cannot yet be relied on to create those sessions for MFA accounts.

Do not copy one session file to another process, and do not let simultaneous processes share one file. Garmin refresh tokens may rotate; concurrent writers can otherwise invalidate or overwrite each other's credentials. For example, use aliases such as personal-dsh, personal-codex, and personal-claude, with an independently initialized session for each one. Do not point another runtime at the same file through a symbolic link or a differently cased path alias.

The processes may share one GARMIN_FIT_DOWNLOAD_DIR parent: the plugin creates a separate account subdirectory from each configured region and email. This also keeps cn and global accounts with the same email from colliding. For example, name the servers garmin-personal and garmin-family and select the intended server in the request.

This is process isolation, not an in-process acco