Skip to content
Conduit

mcp server

catence

Local MCP server for normalized Garmin, Intervals.icu, and Strava data.

rifusaki/catencecommunitylistedunreviewed

Set up

Runs on your computer. Pick your agent and operating system.

Before you start

  • Node.js 20 or newer
~/Library/Application Support/Claude/claude_desktop_config.json
{
  "mcpServers": {
    "rifusaki-catence": {
      "command": "npx",
      "args": [
        "-y",
        "catence"
      ]
    }
  }
}

same format Cursor, GitHub Copilot CLI, Junie, JetBrains AI Assistant, Augment (Auggie CLI), Factory Droid, Kiro, Devin CLI +12

Values in angle brackets are placeholders. Restart Claude Desktop after editing its config.Claude Desktop docs

README

synced Oct 17, 2026

Catence

Local MCP server for primary Garmin data, with added Intervals.icu metrics and Strava enrichment.

... what?

As a context, I'm both an endurance athlete and a data junkie. I already got a coach for all the serious stuff, but I wanted to hook up some—any—LLM to the insane amount of data collected from Garmin, derived on Intervals.icu (I don't pay for TrainingPeaks) and comparable efforts on Strava segments. Naturally, the first answer was a local MCP server for my current tooling.

What is included

  • Garmin, Intervals.icu, and Strava ingestion with source-aware normalization.
  • Read-only MCP tools, plus explicitly named, lock-guarded write tools for Strava hydration and detached syncs.
  • One shared Catence agent can serve several isolated athlete stores. Every personal-data MCP call names an athleteId; Catence never silently combines athletes.
  • A password-protected Chainlit Console with an authenticated dashboard, a data-sync button with live progress, and in-app model management.
  • A generated demo catalog for safe evaluation in Glama, desktop MCP clients, or local development.

Catence helps explore recovery, training load, trends, activity detail, swimming/cycling/running progress, segments, gear, and data quality. It reports the evidence and coverage it can see; it does not diagnose or prescribe training.

Requirements

  • Node.js 22+
  • Python 3.12+ and uv for Garmin and Strava provider workers
  • Provider credentials for the athletes you choose to sync

Quick start

Install Catence and create the first athlete. The default catalog home is ~/.catence; set CATENCE_HOME or pass --home to use a different location.

Every catence-data command requires --athlete <id>; there is no default athlete. Pair it with --home <dir> to select the catalog when not using the default home.

npm install --global catence@latest          # or catence@beta
catence-data setup --athlete alex --label "Alex"

Store each provider value through stdin so it does not enter shell history. Values are written to the selected athlete's owner-only local secret file.

printf %s 'alex@example.com' | catence-data --athlete alex secret set --provider garmin --field email --value-stdin
printf %s 'your-garmin-password' | catence-data --athlete alex secret set --provider garmin --field password --value-stdin
printf %s 'intervals-api-key' | catence-data --athlete alex secret set --provider intervals --field apiKey --value-stdin
printf %s '12345' | catence-data --athlete alex secret set --provider intervals --field athleteId --value-stdin

Sync and create retrieval context:

catence-data --athlete alex sync --provider all
catence-data --athlete alex build-retrieval-index

Start the stdio MCP server:

catence

An agent first calls list_athletes, then includes athleteId with every data tool. This is intentional: a shared agent may access the stores you configured, but no tool implicitly aggregates or crosses between athletes.

Add another athlete

catence-data athlete add --id sam --label "Sam"
printf %s 'sam@example.com' | catence-data --athlete sam secret set --provider garmin --field email --value-stdin
printf %s 'sam-password' | catence-data --athlete sam secret set --provider garmin --field password --value-stdin
catence-data --athlete sam sync --provider garmin
catence-data athlete list

Garmin, Intervals, and Strava client credentials are isolated per athlete. Strava OAuth tokens remain in that athlete's own store. The old 0.1 single-store directory cannot be migrated safely: create a fresh home and re-sync each athlete.

Common operations

catence-data --athlete alex status
catence-data --athlete alex sync --provider intervals
catence-data --athlete alex sync --provider garmin --from 2025-07-29
catence-data --athlete alex backfill --provider garmin --from 2020-01-01 --refresh
catence-data --athlete alex retry --run <run-id>
catence-data --athlete alex progress --watch
catence-data --athlete alex auth strava --callback
catence-data --athlete alex disconnect strava
catence-data update --check
catence-data update

Documentation

DocumentCovers
Deployment: local MCPInstall, catalog, secrets, sync, background progress, stdio/HTTP MCP, demo, common operations
Deployment: local ConsoleLocal web chat setup, model config, doctor, updates, troubleshooting
Deployment: DockerOne-container stack: deploy script, .env, sync.sh, MCP exposure (SSH/Tailscale), Cloudflare Tunnel
ConfigurationComplete config.json schema: rate limits, Strava budget, Console profiles, environment variables
LLM providersModel providers (OpenAI, Anthropic, OpenAI-compatible/Azure, Opencode Go/Zen), reasoning effort
ArchitectureDesign rationale, layers, data flow, serving
MCP activity retrievalImplementation contract for answering questions, current tools, planned endpoints
DistributionRelease artifacts: npm, PyPI, APM, MCPB

MCP clients and HTTP

For a packaged installation, point a client at catence:

codex mcp add catence -- catence
claude mcp add --transport stdio catence -- catence

For a source checkout:

codex mcp add catence -- npm --prefix /absolute/path/to/catence run mcp

Optional local Streamable HTTP MCP and dashboard APIs:

catence serve --host 127.0.0.1 --port 8787

GET /api/v1/athletes returns IDs and labels only. GET /api/v1/dashboard requires athleteId, for example http://127.0.0.1:8787/api/v1/dashboard?athleteId=alex&days=28. Browser origins must be listed with --allow-origin; the packaged Console instead proxies the dashboard through its authenticated same-origin route. The MCP server has no authentication of its own.

Configuration

config.example.json documents rate limits, Strava budgets, and Console model profiles. Keep per-athlete provider values in catence-data secret set, not in .env or config.json. The full config.json schema —including per-model reasoning effort— is documented in docs/configuration.md and docs/llm-providers.md.

For source development:

npm ci
npm run check
npm test
UV_CACHE_DIR=$PWD/.cache/uv uv run --project console --group dev python -m pytest console/tests -q

Data and caveats

The current available sources respond to my own used platforms. I used some wrappers:

Strava is only used for segments and gear.

Caveats

There are two caveats to the data fetching, both coming from the fact that this was created to be used by a single person. Accessing the full Garmin API requires applying via a company—which I don't have—and getting Strava data for multiple users is another headache I don't want to get into.

  • I don't use the official Garmin Connect API. The library uses email/pwd.
  • Connecting Strava requires the user to be a Strava Premium subscriber and create an API application. The rotating token is stored locally in <data-dir>/secrets/strava.json.
↑↓ navigate openesc close