catence
Catence is a local, read-only MCP server that integrates fitness and health data from Garmin, Intervals.icu, and Strava, enabling AI-assisted analysis and reporting. Key capabilities:
Data Discovery & Status: Check sync state, list datasets, schemas, and coverage via catence_status, describe_data, describe_dataset.
Querying & Aggregation: Fetch time-series data with read_series (with downsampling), run aggregations with aggregate_data, or use custom SQL via query_read_only_data.
Statistical Analysis & Modeling: Perform rolling statistics, correlations, trends, and anomalies with analyze_series; fit regression models with fit_series_model.
Activity & Performance: Find activities by filters with find_activities, retrieve FTP/VO2max history, generate cycling and swim progress reports.
Power Analysis: Explore power-duration bests via power_curve_trend and power_coverage_report.
Swimming: Get lap data with get_swim_laps and session comparisons with swim_progress_report.
Strava Enrichment: On-demand hydration of Strava activities and segments (get_activity_segments, hydrate_strava_activity, etc.).
Search: Find relevant context across activities, plans, and messages with search_context.
All tools are read-only except the explicit hydration operations, ensuring data provenance and quality.
Allows syncing and importing canonical activity and health data from Garmin Connect, including activities, daily health metrics, FTP history, VO2max estimates, and more.
Provides targeted enrichment of activities with Strava data, including hydrating individual activities, recent activities, and segment history, using a read-only Strava API allowlist.
Click on "Install 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., "@catenceWhat's my average pace for outdoor runs this year?"
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.
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.
Related MCP server: fitMCP
Scope
Currently a bit limited but I'd like to expand it:
Garmin, Intervals.icu, Strava data fetching
Data normalization and storage
Read-only local MCP server
Local Streamable HTTP server
Release-ready npm, APM, and MCPB distribution artifacts
Generated demo store for safe MCP evaluation
Bundled local Chainlit Console with persisted chat history
Data writing (training sessions, plans)
TrainingPeaks support?
Limited multi-user support (see Caveats)
What Catence can help you do
Catence is most useful when a question benefits from checking several local signals instead of reacting to a single score. The Console and MCP tools can help you explore questions such as:
Recovery and readiness: “What changed in my sleep, HRV, resting heart rate, stress, and recent load before today’s session?”
Training load: “Is this week harder than my recent baseline, and which sessions contributed most?”
Performance trends: “How has my threshold pace, cycling power, or swim efficiency changed across the season?”
Session review: “Compare this long run or interval set with similar recent efforts, including pace/power, heart rate, terrain, and recovery.”
Segments and gear: “Show my history on this climb,” or “which shoes and bikes have carried the most recent training volume?”
Data-quality review: “Which recent activities lack streams, power, health context, or a matching provider record?”
Sources
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.
What I mean by multi-user is being able to manage and serve various users' data on the same server.
Data
Priority is assigned per data type. Missing values mean the provider did not supply that metric.
Data group | Priority and provenance | Data available per activity or date | Aggregated data |
Generic activity data | Garmin (canonical activity data)Intervals.icu (supplemental analysis) | Date/time, timezone, sport, name, distance, elapsed/moving time, elevation, calories, average/max HR, source identity, and quality flags | Activity count, distance, duration, elevation, and calories by date range |
Running — indoor and outdoor | Garmin (activity facts and streams)Intervals.icu (analysis values) | Pace/speed, cadence, HR, power when available, GPS, elevation, grade, splits, intervals, VO₂max, lactate-threshold metrics, and race predictions | Running volume, pace/load trends, and training-metric history |
Cycling — indoor and outdoor | Garmin (activity facts, FIT streams, and settings)Intervals.icu (load, RPE, feel, and weighted power) | Average/weighted power, cadence, speed, HR, GPS, elevation, grade, temperature, cycling dynamics, splits, and FTP observations | Power-duration bests, power coverage, cycling volume/load, and FTP trends |
Swimming — pool and open water | Garmin (explicit lengths and summaries)Intervals.icu (auto-detected sets) | Lengths/laps, pool length, distance, duration, stroke count/rate, SWOLF, average/max HR, sets, and GPS when supplied | Swim volume, session comparisons, and data-completeness summaries |
Structured intervals and sets | Intervals.icu (Intervals-based intervals)Garmin (Garmin splits and sets) | Interval labels, repetitions, duration, moving time, distance, pace, power, HR, intensity, and swim-set details | Interval volume, intensity, and provider-specific comparisons |
Daily health and wellness | Garmin (primary health data) | HR, resting HR, HRV, sleep, sleep score, stress, Body Battery, readiness, SpO₂, weight, steps, hydration, and nutrition | Daily, weekly, and monthly health and nutrition trends |
Strava segments and gear | Strava (source of record) | Segment names, grades, climb category, elevation, effort time/distance, power, HR, cadence, PR/KOM ranks, bikes, shoes, and historic efforts | Segment, climb, effort-history, and gear comparisons |
Derived and cross-source analytics | Garmin (canonical inputs)Intervals.icu (interval inputs)Strava (segment and gear inputs)Catence (derived results) | Reconciled activity values and source-quality flags | Rolling statistics, baselines, correlations, seasonal comparisons, trends, descriptive model fits, and progress reports |
Set up
Catence exposes the same local MCP server over stdio and optional Streamable HTTP. A tagged release publishes the catence npm package, platform-specific MCPB demo bundles, and an APM package. Until that first public release is available, the source-checkout commands below remain fully supported.
Safe one-command demo
After publication, start a no-account MCP server with generated data:
npx --yes catence@0.1.0 demoIt creates ./catence-demo by default and refuses to replace a directory that is not already marked as a Catence demo. The synthetic store deliberately
contains lagged training-load/recovery effects, a multi-signal anomaly period, and some missing sleep-score dates. Every MCP result includes a generated-data disclaimer, so it cannot be mistaken for a personal health record.
From a source checkout, the equivalent command is:
npm run catence-data -- demo
npm run mcp -- demo --data-dir ./catence-demoInstall for live data
After publication, install the package once, then create and synchronize a store that you own:
npm install --global catence@0.1.0
catence-data --data-dir /absolute/path/to/catence-data init
catence-data --data-dir /absolute/path/to/catence-data sync --provider all
catence-data --data-dir /absolute/path/to/catence-data build-retrieval-index
catence --data-dir /absolute/path/to/catence-dataPrerequisites
Node.js 22 or later
Python 3.12 or later and uv for Garmin and Strava provider syncs
Clone the repository, install the Node and Python dependencies, and create your local .env with the following credentials:
Garmin email/password
Intervals.icu API Key and Athlete ID
Strava Client ID and Secret
Create and sync a local data store
By default, runtime data is placed in .catence/ inside the checkout. You can also choose an absolute path outside the repository and supply it consistently with --data-dir (or set CATENCE_DATA_DIR). The examples below use an absolute path.
npm run catence-data -- --data-dir /absolute/path/to/catence-data init
npm run catence-data -- --data-dir /absolute/path/to/catence-data sync --provider all
npm run catence-data -- --data-dir /absolute/path/to/catence-data build-retrieval-indexThe retrieval index is derived context for agent search; rebuild it after a sync. Syncs are incremental after the first import. Use backfill only for an explicit historical range, and add --refresh only when you intentionally want to re-fetch already covered Garmin activity details, files, and streams.
Common commands
Run these from the repository root. Omit --data-dir to use the checkout's .catence/ directory.
# Inspect local coverage and sync individual providers
npm run catence-data -- status
npm run catence-data -- sync --provider intervals
npm run catence-data -- sync --provider garmin --from 2025-07-29
npm run catence-data -- sync --provider strava
npm run catence-data -- sync --provider all
# Recover or import a specific historical range
npm run catence-data -- retry --run <run-id>
npm run catence-data -- backfill --from 2020-01-01
npm run catence-data -- backfill --provider garmin --from 2026-07-01 --refresh
# Refresh derived search context and launch the local MCP server
npm run catence-data -- build-retrieval-index
npm run mcp
npm run mcp -- --data-dir /absolute/path/to/catence-data
# Optional local Streamable HTTP MCP and dashboard API
npm run mcp -- serve --data-dir /absolute/path/to/catence-data --allow-origin http://127.0.0.1:8000
npm run mcp -- serve --allow-origin http://127.0.0.1:8000The first manual sync uses the previous 12 months only when there is no local normalized coverage. sync --provider strava refreshes gear data. Strava activity segments and gear are enriched on demand by the MCP tools.
Connect Strava
With STRAVA_CLIENT_ID and STRAVA_CLIENT_SECRET in .env, the local callback flow avoids copying a code out of the browser URL. Register http://127.0.0.1:8765/strava/callback as the authorization callback in the Strava application, then run:
npm run catence-data -- auth strava --callbackThe command prints the authorization URL to the terminal, waits up to five minutes for the browser callback, validates OAuth state, and stores the resulting token only in <data-dir>/secrets/strava.json.
The existing manual flow remains available for headless use:
npm run catence-data -- auth strava
npm run catence-data -- auth strava --code <authorization-code>Catence requests read, activity:read_all, and read_all. To remove the stored Strava connection later, run:
npm run catence-data -- disconnect stravaRun the local Console
The Console is Catence’s local chat experience. It sends model calls through LiteLLM in-process and calls the same Streamable HTTP MCP tools that a coding agent uses. Provider keys are never written to Catence configuration or chat storage.
Install and launch
After publication, the Console is a separate Python distribution. It includes the Catence-maintained Chainlit frontend and starts the matching npm runtime on loopback automatically:
uvx catence-console@0.1.0 serve --data-dir /absolute/path/to/catence-dataThe command needs Node.js 22 and npx, but does not require a Catence or catence-ui checkout. Pass --mcp-url http://127.0.0.1:8787/mcp when you intentionally want to use an already-running compatible Catence runtime.
For source development, install the locally built catence-chainlit wheel and run uv run --project console catence-console serve from this repository.
First-run setup
Launch the Console once with your selected provider’s environment variables in the terminal. If <data-dir>/config.json has no Console section, the browser walks through provider and model selection (Azure, OpenAI, or Anthropic) and writes only non-secret configuration. For example:
# Azure OpenAI / Foundry: use the Azure resource root and the Responses API route.
export AZURE_API_KEY='…'
export AZURE_API_BASE='https://your-resource.openai.azure.com'
export AZURE_API_VERSION='preview'
uvx catence-console@0.1.0 serve --data-dir /absolute/path/to/catence-dataFor an existing configuration or an automated setup, add a console section to <data-dir>/config.json. It defines named providers and deployments and environment-variable names, never credential values. The complete shape is in config.example.json. For the two common direct providers, the minimal profiles are:
{
"openai": {
"label": "OpenAI",
"model": "openai/gpt-5-mini",
"apiKeyEnv": "OPENAI_API_KEY"
},
"anthropic": {
"label": "Anthropic",
"model": "anthropic/claude-sonnet-4-5",
"apiKeyEnv": "ANTHROPIC_API_KEY"
}
}A provider with several deployments looks like this:
{
"console": {
"defaultProfile": "provider",
"limits": {
"toolRounds": 8,
"toolResultCharacters": 24000
},
"profiles": {
"azure-foundry": {
"label": "Provider",
"defaultModel": "terra",
"defaultReasoningEffort": "medium",
"models": {
"terra": { "label": "GPT-5.6 Terra", "model": "provider/gpt-5.6-terra" },
"luna": { "label": "GPT-5.6 Luna", "model": "provider/gpt-5.6-luna" },
"sol": { "label": "GPT-5.6 Sol", "model": "provider/gpt-5.6-sol" }
},
"apiKeyEnv": "API_KEY",
"apiBaseEnv": "API_BASE",
"apiVersionEnv": "API_VERSION"
}
}
}
}An OpenAI-compatible provider such as OpenCode uses a model such as openai/your-model plus apiBaseEnv and, if needed, apiKeyEnv. LiteLLM handles provider normalization.
The Console shows all configured deployments in its Model selector and sends the selected Thinking effort to LiteLLM as reasoning_effort. Choose only efforts supported by the selected deployment. Existing profiles with a single model remain supported.
Launching
Set the referenced variables in the terminal that will run the Console, then launch everything on loopback with one command:
uvx catence-console@0.1.0 serve --data-dir /absolute/path/to/catence-dataOn its first normal launch this starts the matching Catence runtime at http://127.0.0.1:8787, permits only the local Console origins, and serves the prebuilt chat and dashboard at http://127.0.0.1:8000.
To preflight a profile and an already-running Catence HTTP server, use:
uvx catence-console@0.1.0 doctor \
--data-dir /absolute/path/to/catence-data \
--mcp-url http://127.0.0.1:8787/mcpdoctor reports profile IDs, models, missing environment-variable names, and Catence health.
Local chat history
Console chats, messages, tool steps, and thread metadata are persisted at <data-dir>/console/chat-history.sqlite3. A later turn receives a compact list of prior calls (not their results); it can lazily load one saved result when it is material, or repeat the authoritative call for fresh data.
Add Catence to an MCP client
First complete the setup and at least one sync. For a packaged install, point a client to the installed catence binary (or use the safe demo command):
catence --data-dir /absolute/path/to/catence-data
npx --yes catence@0.1.0 demoFor a source checkout, each client should start the same local command, pointing at the same absolute data directory:
npm --prefix /absolute/path/to/catence run mcp -- --data-dir /absolute/path/to/catence-dataThe MCP server's ordinary reads are local and read-only. Its explicit Strava hydration tools are the exception; they use the already stored local connection and a shared write lock. catence serve retains the same tools at /mcp and also exposes loopback dashboard data at /api/v1/dashboard; browser origins must be allowed explicitly with --allow-origin.
Codex
For a first look, add the generated demo server from the terminal:
codex mcp add catence-demo -- npx --yes catence@0.1.0 demoFor a live npm install, use:
codex mcp add catence -- catence --data-dir /absolute/path/to/catence-dataFor a source checkout, use:
codex mcp add catence -- npm --prefix /absolute/path/to/catence run mcp -- --data-dir /absolute/path/to/catence-dataAlternatively, add this to ~/.codex/config.toml for a personal installation, or .codex/config.toml in a trusted repository:
[mcp_servers.catence]
command = "npm"
args = ["--prefix", "/absolute/path/to/catence", "run", "mcp", "--", "--data-dir", "/absolute/path/to/catence-data"]
startup_timeout_sec = 30OpenCode
Add this local MCP definition to opencode.json or opencode.jsonc:
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"catence": {
"type": "local",
"command": [
"npm",
"--prefix",
"/absolute/path/to/catence",
"run",
"mcp",
"--",
"--data-dir",
"/absolute/path/to/catence-data"
],
"enabled": true
}
}
}Claude Code
Add the local stdio server from a terminal:
claude mcp add --transport stdio catence -- npm --prefix /absolute/path/to/catence run mcp -- --data-dir /absolute/path/to/catence-dataUse Claude Code's MCP listing command to confirm it was added:
claude mcp listAPM and MCPB
The repository's apm.yml registers both catence and the safe catence-demo server with supported agent clients. After a tag is published:
apm install rifusaki/catence#v0.1.0Each release also attaches platform-specific catence-demo-*.mcpb files. Open the matching file in an MCPB-capable desktop client (including Claude Desktop) to install the one-click generated demo. Live-source setup remains an explicit CLI step, so the bundle never requests or stores provider credentials.
Wellness shortcuts
Catence keeps its flexible catalog and analytical tools, and now exposes four small opinionated wellness tools for common recovery questions:
wellness_correlatecompares curated recovery/training metrics and can scan lags from −7 to +7 days.wellness_baselinesreturns a trailing mean, standard-deviation band, latest value, and latest z-score.wellness_anomaliesfinds statistical outliers and dates with multiple unusual signals; it does not diagnose or prescribe.wellness_coverageshows absent dates and unresolved extraction errors without assuming that missing data means rest or a health outcome.
They use the same source-aware daily_health and canonical_activity_training projections as the general query tools. See distribution and release notes for the current tooling.
Maintenance
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceMCP server that exposes Garmin Connect health and activity data (steps, sleep, stress, activities, etc.) via tools for querying, analysis, and visualization.Apache 2.0
- FlicenseNot gradedqualityBmaintenanceA multi-platform fitness MCP server that syncs data from Garmin, Strava, Google Fit, and Suunto into a local DuckDB database and provides analytics tools via MCP.1
- AlicenseBqualityAmaintenancePrivacy-first MCP server for Strava activities, streams, routes and training data.292162MIT
- AlicenseAqualityBmaintenanceMCP server for reading and querying Garmin Connect data, including activities, strength history, recovery, trends, and optionally creating workouts.12MIT
Related MCP Connectors
MCP server for Withings health data — sleep, activity, heart, and body metrics.
Garmin data in Claude & ChatGPT via the Garmin Health API. OAuth sign-in, no password sharing.
MCP server for interacting with the Supabase platform
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/rifusaki/catence'
If you have feedback or need assistance with the MCP directory API, please join our Discord server