catence 0.0.0 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +373 -29
  3. package/config.example.json +63 -0
  4. package/dist/contracts/index.d.ts +4 -0
  5. package/dist/contracts/index.js +5 -0
  6. package/dist/contracts/release.d.ts +20 -0
  7. package/dist/contracts/release.js +17 -0
  8. package/dist/contracts/runtime.d.ts +11 -0
  9. package/dist/contracts/runtime.js +2 -0
  10. package/dist/contracts/staging.d.ts +183 -0
  11. package/dist/contracts/staging.js +36 -0
  12. package/dist/contracts/storage.d.ts +15 -0
  13. package/dist/contracts/storage.js +2 -0
  14. package/dist/core/dashboard/snapshot.d.ts +16 -0
  15. package/dist/core/dashboard/snapshot.js +65 -0
  16. package/dist/core/query/activity-discovery.d.ts +21 -0
  17. package/dist/core/query/activity-discovery.js +133 -0
  18. package/dist/core/query/analytics.d.ts +59 -0
  19. package/dist/core/query/analytics.js +395 -0
  20. package/dist/core/query/catalog.d.ts +26 -0
  21. package/dist/core/query/catalog.js +141 -0
  22. package/dist/core/query/fitness.d.ts +40 -0
  23. package/dist/core/query/fitness.js +297 -0
  24. package/dist/core/query/repository.d.ts +19 -0
  25. package/dist/core/query/repository.js +182 -0
  26. package/dist/core/query/sql-guard.d.ts +7 -0
  27. package/dist/core/query/sql-guard.js +121 -0
  28. package/dist/core/query/sql.d.ts +2 -0
  29. package/dist/core/query/sql.js +5 -0
  30. package/dist/core/query/swimming.d.ts +19 -0
  31. package/dist/core/query/swimming.js +184 -0
  32. package/dist/core/query/wellness.d.ts +45 -0
  33. package/dist/core/query/wellness.js +292 -0
  34. package/dist/core/retrieval/index.d.ts +13 -0
  35. package/dist/core/retrieval/index.js +248 -0
  36. package/dist/core/runtime/configuration.d.ts +71 -0
  37. package/dist/core/runtime/configuration.js +106 -0
  38. package/dist/core/runtime/limiter.d.ts +13 -0
  39. package/dist/core/runtime/limiter.js +17 -0
  40. package/dist/elt/application/demo.d.ts +23 -0
  41. package/dist/elt/application/demo.js +165 -0
  42. package/dist/elt/application/management.d.ts +25 -0
  43. package/dist/elt/application/management.js +283 -0
  44. package/dist/elt/ingestion/importer.d.ts +8 -0
  45. package/dist/elt/ingestion/importer.js +87 -0
  46. package/dist/elt/ingestion/providers/intervals.d.ts +21 -0
  47. package/dist/elt/ingestion/providers/intervals.js +224 -0
  48. package/dist/elt/ingestion/providers/registry.d.ts +19 -0
  49. package/dist/elt/ingestion/providers/registry.js +62 -0
  50. package/dist/elt/ingestion/providers/strava/service.d.ts +42 -0
  51. package/dist/elt/ingestion/providers/strava/service.js +365 -0
  52. package/dist/elt/normalization/activities/linking.d.ts +5 -0
  53. package/dist/elt/normalization/activities/linking.js +165 -0
  54. package/dist/elt/normalization/normalizers.d.ts +5 -0
  55. package/dist/elt/normalization/normalizers.js +816 -0
  56. package/dist/elt/normalization/segments/strava-normalizer.d.ts +3 -0
  57. package/dist/elt/normalization/segments/strava-normalizer.js +93 -0
  58. package/dist/elt/storage/artifacts.d.ts +13 -0
  59. package/dist/elt/storage/artifacts.js +48 -0
  60. package/dist/elt/storage/database.d.ts +74 -0
  61. package/dist/elt/storage/database.js +296 -0
  62. package/dist/elt/storage/migrations.d.ts +3 -0
  63. package/dist/elt/storage/migrations.js +832 -0
  64. package/dist/elt/storage/migrations.legacy.d.ts +12 -0
  65. package/dist/elt/storage/migrations.legacy.js +332 -0
  66. package/dist/elt/storage/sql.d.ts +2 -0
  67. package/dist/elt/storage/sql.js +7 -0
  68. package/dist/elt/storage/write-lock.d.ts +6 -0
  69. package/dist/elt/storage/write-lock.js +28 -0
  70. package/dist/elt/streams.d.ts +24 -0
  71. package/dist/elt/streams.js +62 -0
  72. package/dist/interfaces/cli/commands.d.ts +2 -0
  73. package/dist/interfaces/cli/commands.js +101 -0
  74. package/dist/interfaces/cli/main.d.ts +2 -0
  75. package/dist/interfaces/cli/main.js +3 -0
  76. package/dist/interfaces/http/cli-options.d.ts +9 -0
  77. package/dist/interfaces/http/cli-options.js +81 -0
  78. package/dist/interfaces/http/server.d.ts +14 -0
  79. package/dist/interfaces/http/server.js +169 -0
  80. package/dist/interfaces/mcp/cli-options.d.ts +6 -0
  81. package/dist/interfaces/mcp/cli-options.js +36 -0
  82. package/dist/interfaces/mcp/main.d.ts +2 -0
  83. package/dist/interfaces/mcp/main.js +59 -0
  84. package/dist/interfaces/mcp/server.d.ts +8 -0
  85. package/dist/interfaces/mcp/server.js +521 -0
  86. package/dist/runtime/index.d.ts +29 -0
  87. package/dist/runtime/index.js +19 -0
  88. package/docs/architecture.md +100 -0
  89. package/docs/assets/image.png +0 -0
  90. package/docs/mcp-activity-retrieval.md +377 -0
  91. package/package.json +81 -6
  92. package/pyproject.toml +28 -0
  93. package/python/__init__.py +1 -0
  94. package/python/catence/__init__.py +0 -0
  95. package/python/catence/providers/__init__.py +0 -0
  96. package/python/catence/providers/garmin/__init__.py +3 -0
  97. package/python/catence/providers/garmin/cli.py +62 -0
  98. package/python/catence/providers/garmin/contracts.py +23 -0
  99. package/python/catence/providers/garmin/registry.py +48 -0
  100. package/python/catence/providers/garmin/staging.py +87 -0
  101. package/python/catence/providers/garmin/streams.py +195 -0
  102. package/python/catence/providers/garmin/worker.py +450 -0
  103. package/python/catence/providers/strava/__init__.py +3 -0
  104. package/python/catence/providers/strava/cli.py +404 -0
  105. package/python/catence/providers/strava/staging.py +73 -0
  106. package/uv.lock +708 -0
package/README.md CHANGED
@@ -1,45 +1,389 @@
1
- # catence
1
+ # Catence
2
2
 
3
- ## ⚠️ IMPORTANT NOTICE ⚠️
3
+ Local MCP server for primary Garmin data, with added Intervals.icu metrics and Strava enrichment.
4
4
 
5
- **This package is created solely for the purpose of setting up OIDC (OpenID Connect) trusted publishing with npm.**
5
+ ![](docs/assets/image.png)
6
6
 
7
- This is **NOT** a functional package and contains **NO** code or functionality beyond the OIDC setup configuration.
7
+ ## What?
8
8
 
9
- ## Purpose
9
+ 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.
10
10
 
11
- This package exists to:
12
- 1. Configure OIDC trusted publishing for the package name `catence`
13
- 2. Enable secure, token-less publishing from CI/CD workflows
14
- 3. Establish provenance for packages published under this name
11
+ ## Scope
15
12
 
16
- ## What is OIDC Trusted Publishing?
13
+ Currently a bit limited but I'd like to expand it:
17
14
 
18
- OIDC trusted publishing allows package maintainers to publish packages directly from their CI/CD workflows without needing to manage npm access tokens. Instead, it uses OpenID Connect to establish trust between the CI/CD provider (like GitHub Actions) and npm.
15
+ - [x] Garmin, Intervals.icu, Strava data fetching
16
+ - [x] Data normalization and storage
17
+ - [x] Read-only local MCP server
18
+ - [x] Local Streamable HTTP server
19
+ - [x] Release-ready npm, [APM](https://github.com/microsoft/apm), and MCPB distribution artifacts
20
+ - [x] Generated demo store for safe MCP evaluation
21
+ - [x] Bundled local Chainlit Console with persisted chat history
22
+ - [ ] Data writing (training sessions, plans)
23
+ - [ ] TrainingPeaks support?
24
+ - [ ] Limited multi-user support (see Caveats)
19
25
 
20
- ## Setup Instructions
26
+ ## What Catence can help you do
21
27
 
22
- To properly configure OIDC trusted publishing for this package:
28
+ 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:
23
29
 
24
- 1. Go to [npmjs.com](https://www.npmjs.com/) and navigate to your package settings
25
- 2. Configure the trusted publisher (e.g., GitHub Actions)
26
- 3. Specify the repository and workflow that should be allowed to publish
27
- 4. Use the configured workflow to publish your actual package
30
+ - **Recovery and readiness:** “What changed in my sleep, HRV, resting heart rate, stress, and recent load before today’s session?”
31
+ - **Training load:** “Is this week harder than my recent baseline, and which sessions contributed most?”
32
+ - **Performance trends:** “How has my threshold pace, cycling power, or swim efficiency changed across the season?”
33
+ - **Session review:** “Compare this long run or interval set with similar recent efforts, including pace/power, heart rate, terrain, and recovery.”
34
+ - **Segments and gear:** “Show my history on this climb,” or “which shoes and bikes have carried the most recent training volume?”
35
+ - **Data-quality review:** “Which recent activities lack streams, power, health context, or a matching provider record?”
28
36
 
29
- ## DO NOT USE THIS PACKAGE
37
+ ## Sources
30
38
 
31
- This package is a placeholder for OIDC configuration only. It:
32
- - Contains no executable code
33
- - Provides no functionality
34
- - Should not be installed as a dependency
35
- - Exists only for administrative purposes
39
+ The current available sources respond to my own used platforms. I used some wrappers:
36
40
 
37
- ## More Information
41
+ - [cyberjunky/python-garminconnect](https://github.com/cyberjunky/python-garminconnect)
42
+ - [paladini/node-intervals-icu](https://github.com/paladini/node-intervals-icu)
43
+ - [stravalib/stravalib](https://github.com/stravalib/stravalib)
38
44
 
39
- For more details about npm's trusted publishing feature, see:
40
- - [npm Trusted Publishing Documentation](https://docs.npmjs.com/generating-provenance-statements)
41
- - [GitHub Actions OIDC Documentation](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect)
45
+ Strava is only used for segments and gear.
42
46
 
43
- ---
47
+ ### Caveats
44
48
 
45
- **Maintained for OIDC setup purposes only**
49
+ 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.
50
+
51
+ - I don't use the official Garmin Connect API. The library uses email/pwd.
52
+ - Connecting Strava requires the user to be a Strava Premium subscriber and create an [API application](https://stravalib.readthedocs.io/en/latest/get-started/authenticate-with-strava.html). The rotating token is stored locally in
53
+ `<data-dir>/secrets/strava.json`.
54
+
55
+ What I mean by multi-user is being able to manage and serve various users' data on the same server.
56
+
57
+ ## Data
58
+
59
+ Priority is assigned per data type. Missing values mean the provider did not supply that metric.
60
+
61
+ | Data group | Priority and provenance | Data available per activity or date | Aggregated data |
62
+ | --- | --- | --- | --- |
63
+ | Generic activity data | <ol><li>Garmin (canonical activity data)</li><li>Intervals.icu (supplemental analysis)</li></ol> | 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 |
64
+ | Running — indoor and outdoor | <ol><li>Garmin (activity facts and streams)</li><li>Intervals.icu (analysis values)</li></ol> | 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 |
65
+ | Cycling — indoor and outdoor | <ol><li>Garmin (activity facts, FIT streams, and settings)</li><li>Intervals.icu (load, RPE, feel, and weighted power)</li></ol> | 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 |
66
+ | Swimming — pool and open water | <ol><li>Garmin (explicit lengths and summaries)</li><li>Intervals.icu (auto-detected sets)</li></ol> | 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 |
67
+ | Structured intervals and sets | <ol><li>Intervals.icu (Intervals-based intervals)</li><li>Garmin (Garmin splits and sets)</li></ol> | Interval labels, repetitions, duration, moving time, distance, pace, power, HR, intensity, and swim-set details | Interval volume, intensity, and provider-specific comparisons |
68
+ | Daily health and wellness | <ol><li>Garmin (primary health data)</li></ol> | 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 |
69
+ | Strava segments and gear | <ol><li>Strava (source of record)</li></ol> | 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 |
70
+ | Derived and cross-source analytics | <ol><li>Garmin (canonical inputs)</li><li>Intervals.icu (interval inputs)</li><li>Strava (segment and gear inputs)</li><li>Catence (derived results)</li></ol> | Reconciled activity values and source-quality flags | Rolling statistics, baselines, correlations, seasonal comparisons, trends, descriptive model fits, and progress reports |
71
+
72
+ ## Set up
73
+
74
+ 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.
75
+
76
+ ### Safe one-command demo
77
+
78
+ After publication, start a no-account MCP server with generated data:
79
+
80
+ ```sh
81
+ npx --yes catence@0.1.0 demo
82
+ ```
83
+
84
+ It creates `./catence-demo` by default and refuses to replace a directory that is not already marked as a Catence demo. The synthetic store deliberately
85
+ 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.
86
+
87
+ From a source checkout, the equivalent command is:
88
+
89
+ ```sh
90
+ npm run catence-data -- demo
91
+ npm run mcp -- demo --data-dir ./catence-demo
92
+ ```
93
+
94
+ ### Install for live data
95
+
96
+ After publication, install the package once, then create and synchronize a store that you own:
97
+
98
+ ```sh
99
+ npm install --global catence@0.1.0
100
+ catence-data --data-dir /absolute/path/to/catence-data init
101
+ catence-data --data-dir /absolute/path/to/catence-data sync --provider all
102
+ catence-data --data-dir /absolute/path/to/catence-data build-retrieval-index
103
+ catence --data-dir /absolute/path/to/catence-data
104
+ ```
105
+
106
+ ### Prerequisites
107
+
108
+ - Node.js 22 or later
109
+ - Python 3.12 or later and [uv](https://docs.astral.sh/uv/) for Garmin and Strava provider syncs
110
+
111
+ Clone the repository, install the Node and Python dependencies, and create your local `.env` with the following credentials:
112
+
113
+ - Garmin email/password
114
+ - Intervals.icu [API Key and Athlete ID](https://forum.intervals.icu/t/api-access-to-intervals-icu/609)
115
+ - Strava [Client ID and Secret](https://stravalib.readthedocs.io/en/latest/get-started/authenticate-with-strava.html)
116
+
117
+ ### Create and sync a local data store
118
+
119
+ 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.
120
+
121
+ ```sh
122
+ npm run catence-data -- --data-dir /absolute/path/to/catence-data init
123
+ npm run catence-data -- --data-dir /absolute/path/to/catence-data sync --provider all
124
+ npm run catence-data -- --data-dir /absolute/path/to/catence-data build-retrieval-index
125
+ ```
126
+
127
+ The 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.
128
+
129
+ ### Common commands
130
+
131
+ Run these from the repository root. Omit `--data-dir` to use the checkout's `.catence/` directory.
132
+
133
+ ```sh
134
+ # Inspect local coverage and sync individual providers
135
+ npm run catence-data -- status
136
+ npm run catence-data -- sync --provider intervals
137
+ npm run catence-data -- sync --provider garmin --from 2025-07-29
138
+ npm run catence-data -- sync --provider strava
139
+ npm run catence-data -- sync --provider all
140
+
141
+ # Recover or import a specific historical range
142
+ npm run catence-data -- retry --run <run-id>
143
+ npm run catence-data -- backfill --from 2020-01-01
144
+ npm run catence-data -- backfill --provider garmin --from 2026-07-01 --refresh
145
+
146
+ # Refresh derived search context and launch the local MCP server
147
+ npm run catence-data -- build-retrieval-index
148
+ npm run mcp
149
+ npm run mcp -- --data-dir /absolute/path/to/catence-data
150
+
151
+ # Optional local Streamable HTTP MCP and dashboard API
152
+ npm run mcp -- serve --data-dir /absolute/path/to/catence-data --allow-origin http://127.0.0.1:8000
153
+ npm run mcp -- serve --allow-origin http://127.0.0.1:8000
154
+ ```
155
+
156
+ The 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.
157
+
158
+ ### Connect Strava
159
+
160
+ 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:
161
+
162
+ ```sh
163
+ npm run catence-data -- auth strava --callback
164
+ ```
165
+
166
+ The 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`.
167
+
168
+ The existing manual flow remains available for headless use:
169
+
170
+ ```sh
171
+ npm run catence-data -- auth strava
172
+ npm run catence-data -- auth strava --code <authorization-code>
173
+ ```
174
+
175
+ Catence requests `read`, `activity:read_all`, and `read_all`. To remove the stored Strava connection later, run:
176
+
177
+ ```sh
178
+ npm run catence-data -- disconnect strava
179
+ ```
180
+
181
+ ## Run the local Console
182
+
183
+ 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.
184
+
185
+ ### Install and launch
186
+
187
+ 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:
188
+
189
+ ```sh
190
+ uvx catence-console@0.1.0 serve --data-dir /absolute/path/to/catence-data
191
+ ```
192
+
193
+ The 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.
194
+
195
+ For source development, install the locally built `catence-chainlit` wheel and run `uv run --project console catence-console serve` from this repository.
196
+
197
+ ### First-run setup
198
+
199
+ 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:
200
+
201
+ ```sh
202
+ # Azure OpenAI / Foundry: use the Azure resource root and the Responses API route.
203
+ export AZURE_API_KEY='…'
204
+ export AZURE_API_BASE='https://your-resource.openai.azure.com'
205
+ export AZURE_API_VERSION='preview'
206
+
207
+ uvx catence-console@0.1.0 serve --data-dir /absolute/path/to/catence-data
208
+ ```
209
+
210
+ For 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`](config.example.json). For the two common direct providers, the minimal profiles are:
211
+
212
+ ```json
213
+ {
214
+ "openai": {
215
+ "label": "OpenAI",
216
+ "model": "openai/gpt-5-mini",
217
+ "apiKeyEnv": "OPENAI_API_KEY"
218
+ },
219
+ "anthropic": {
220
+ "label": "Anthropic",
221
+ "model": "anthropic/claude-sonnet-4-5",
222
+ "apiKeyEnv": "ANTHROPIC_API_KEY"
223
+ }
224
+ }
225
+ ```
226
+
227
+ A provider with several deployments looks like this:
228
+
229
+ ```json
230
+ {
231
+ "console": {
232
+ "defaultProfile": "provider",
233
+ "limits": {
234
+ "toolRounds": 8,
235
+ "toolResultCharacters": 24000
236
+ },
237
+ "profiles": {
238
+ "azure-foundry": {
239
+ "label": "Provider",
240
+ "defaultModel": "terra",
241
+ "defaultReasoningEffort": "medium",
242
+ "models": {
243
+ "terra": { "label": "GPT-5.6 Terra", "model": "provider/gpt-5.6-terra" },
244
+ "luna": { "label": "GPT-5.6 Luna", "model": "provider/gpt-5.6-luna" },
245
+ "sol": { "label": "GPT-5.6 Sol", "model": "provider/gpt-5.6-sol" }
246
+ },
247
+ "apiKeyEnv": "API_KEY",
248
+ "apiBaseEnv": "API_BASE",
249
+ "apiVersionEnv": "API_VERSION"
250
+ }
251
+ }
252
+ }
253
+ }
254
+ ```
255
+
256
+ 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.
257
+
258
+ 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.
259
+
260
+ ### Launching
261
+
262
+ Set the referenced variables in the terminal that will run the Console, then launch everything on loopback with one command:
263
+
264
+ ```sh
265
+ uvx catence-console@0.1.0 serve --data-dir /absolute/path/to/catence-data
266
+ ```
267
+
268
+ On 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`.
269
+
270
+ To preflight a profile and an already-running Catence HTTP server, use:
271
+
272
+ ```sh
273
+ uvx catence-console@0.1.0 doctor \
274
+ --data-dir /absolute/path/to/catence-data \
275
+ --mcp-url http://127.0.0.1:8787/mcp
276
+ ```
277
+
278
+ `doctor` reports profile IDs, models, missing environment-variable names, and Catence health.
279
+
280
+ ### Local chat history
281
+
282
+ 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.
283
+
284
+ ## Add Catence to an MCP client
285
+
286
+ 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):
287
+
288
+ ```sh
289
+ catence --data-dir /absolute/path/to/catence-data
290
+ npx --yes catence@0.1.0 demo
291
+ ```
292
+
293
+ For a source checkout, each client should start the same local command, pointing at the same absolute data directory:
294
+
295
+ ```sh
296
+ npm --prefix /absolute/path/to/catence run mcp -- --data-dir /absolute/path/to/catence-data
297
+ ```
298
+
299
+ The 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`.
300
+
301
+ ### Codex
302
+
303
+ For a first look, add the generated demo server from the terminal:
304
+
305
+ ```sh
306
+ codex mcp add catence-demo -- npx --yes catence@0.1.0 demo
307
+ ```
308
+
309
+ For a live npm install, use:
310
+
311
+ ```sh
312
+ codex mcp add catence -- catence --data-dir /absolute/path/to/catence-data
313
+ ```
314
+
315
+ For a source checkout, use:
316
+
317
+ ```sh
318
+ codex mcp add catence -- npm --prefix /absolute/path/to/catence run mcp -- --data-dir /absolute/path/to/catence-data
319
+ ```
320
+
321
+ Alternatively, add this to `~/.codex/config.toml` for a personal installation, or `.codex/config.toml` in a trusted repository:
322
+
323
+ ```toml
324
+ [mcp_servers.catence]
325
+ command = "npm"
326
+ args = ["--prefix", "/absolute/path/to/catence", "run", "mcp", "--", "--data-dir", "/absolute/path/to/catence-data"]
327
+ startup_timeout_sec = 30
328
+ ```
329
+
330
+ ### OpenCode
331
+
332
+ Add this local MCP definition to `opencode.json` or `opencode.jsonc`:
333
+
334
+ ```jsonc
335
+ {
336
+ "$schema": "https://opencode.ai/config.json",
337
+ "mcp": {
338
+ "catence": {
339
+ "type": "local",
340
+ "command": [
341
+ "npm",
342
+ "--prefix",
343
+ "/absolute/path/to/catence",
344
+ "run",
345
+ "mcp",
346
+ "--",
347
+ "--data-dir",
348
+ "/absolute/path/to/catence-data"
349
+ ],
350
+ "enabled": true
351
+ }
352
+ }
353
+ }
354
+ ```
355
+
356
+ ### Claude Code
357
+
358
+ Add the local stdio server from a terminal:
359
+
360
+ ```sh
361
+ claude mcp add --transport stdio catence -- npm --prefix /absolute/path/to/catence run mcp -- --data-dir /absolute/path/to/catence-data
362
+ ```
363
+
364
+ Use Claude Code's MCP listing command to confirm it was added:
365
+
366
+ ```sh
367
+ claude mcp list
368
+ ```
369
+
370
+ ### APM and MCPB
371
+
372
+ The repository's [`apm.yml`](apm.yml) registers both `catence` and the safe `catence-demo` server with supported agent clients. After a tag is published:
373
+
374
+ ```sh
375
+ apm install rifusaki/catence#v0.1.0
376
+ ```
377
+
378
+ Each 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.
379
+
380
+ ## Wellness shortcuts
381
+
382
+ Catence keeps its flexible catalog and analytical tools, and now exposes four small opinionated wellness tools for common recovery questions:
383
+
384
+ - `wellness_correlate` compares curated recovery/training metrics and can scan lags from −7 to +7 days.
385
+ - `wellness_baselines` returns a trailing mean, standard-deviation band, latest value, and latest z-score.
386
+ - `wellness_anomalies` finds statistical outliers and dates with multiple unusual signals; it does not diagnose or prescribe.
387
+ - `wellness_coverage` shows absent dates and unresolved extraction errors without assuming that missing data means rest or a health outcome.
388
+
389
+ They use the same source-aware `daily_health` and `canonical_activity_training` projections as the general query tools. See [distribution and release notes](docs/distribution.md) for the current tooling.
@@ -0,0 +1,63 @@
1
+ {
2
+ "mcp": {
3
+ "rateLimits": {
4
+ "server": { "requests": 240, "windowSeconds": 60 },
5
+ "tools": {
6
+ "*": { "requests": 60, "windowSeconds": 60 },
7
+ "hydrate_strava_activity": { "requests": 6, "windowSeconds": 60 },
8
+ "hydrate_recent_strava_activities": { "requests": 1, "windowSeconds": 60 },
9
+ "hydrate_strava_segment_history": { "requests": 4, "windowSeconds": 60 }
10
+ },
11
+ "resources": {
12
+ "*": { "requests": 120, "windowSeconds": 60 }
13
+ }
14
+ }
15
+ },
16
+ "providers": {
17
+ "strava": {
18
+ "budget": {
19
+ "maxConcurrentRequests": 1,
20
+ "readRequestsPer15Minutes": 80,
21
+ "readRequestsPerDay": 800
22
+ }
23
+ }
24
+ },
25
+ "console": {
26
+ "defaultProfile": "azure-foundry",
27
+ "limits": {
28
+ "toolRounds": 8,
29
+ "toolResultCharacters": 24000
30
+ },
31
+ "profiles": {
32
+ "azure-foundry": {
33
+ "label": "Azure Foundry",
34
+ "defaultModel": "terra",
35
+ "defaultReasoningEffort": "medium",
36
+ "models": {
37
+ "terra": { "label": "GPT-5.6 Terra", "model": "azure_ai/gpt-5.6-terra" },
38
+ "luna": { "label": "GPT-5.6 Luna", "model": "azure_ai/gpt-5.6-luna" },
39
+ "sol": { "label": "GPT-5.6 Sol", "model": "azure_ai/gpt-5.6-sol" }
40
+ },
41
+ "apiKeyEnv": "AZURE_API_KEY",
42
+ "apiBaseEnv": "AZURE_API_BASE",
43
+ "apiVersionEnv": "AZURE_API_VERSION"
44
+ },
45
+ "opencode": {
46
+ "label": "OpenCode-compatible",
47
+ "model": "openai/your-model-name",
48
+ "apiKeyEnv": "OPENCODE_API_KEY",
49
+ "apiBaseEnv": "OPENCODE_API_BASE"
50
+ },
51
+ "openai": {
52
+ "label": "OpenAI",
53
+ "model": "openai/gpt-5-mini",
54
+ "apiKeyEnv": "OPENAI_API_KEY"
55
+ },
56
+ "anthropic": {
57
+ "label": "Anthropic",
58
+ "model": "anthropic/claude-sonnet-4-5",
59
+ "apiKeyEnv": "ANTHROPIC_API_KEY"
60
+ }
61
+ }
62
+ }
63
+ }
@@ -0,0 +1,4 @@
1
+ export * from './runtime.js';
2
+ export * from './release.js';
3
+ export * from './staging.js';
4
+ export * from './storage.js';
@@ -0,0 +1,5 @@
1
+ export * from './runtime.js';
2
+ export * from './release.js';
3
+ export * from './staging.js';
4
+ export * from './storage.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Stable runtime contract used by independently distributed Catence clients.
3
+ * Keep these values synchronized with release/manifest.json; release checks
4
+ * deliberately reject drift before a package can be published.
5
+ */
6
+ export declare const CATENCE_RUNTIME_VERSION = "0.1.0";
7
+ export declare const CATENCE_PROTOCOL_VERSION = 1;
8
+ export type CatenceRuntimeCapabilities = {
9
+ mcp: true;
10
+ dashboardApi: 1;
11
+ demoStore: true;
12
+ };
13
+ export type CatenceRuntimeHealth = {
14
+ status: 'ok';
15
+ service: 'catence';
16
+ runtimeVersion: string;
17
+ protocolVersion: number;
18
+ capabilities: CatenceRuntimeCapabilities;
19
+ };
20
+ export declare function catenceRuntimeHealth(): CatenceRuntimeHealth;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Stable runtime contract used by independently distributed Catence clients.
3
+ * Keep these values synchronized with release/manifest.json; release checks
4
+ * deliberately reject drift before a package can be published.
5
+ */
6
+ export const CATENCE_RUNTIME_VERSION = '0.1.0';
7
+ export const CATENCE_PROTOCOL_VERSION = 1;
8
+ export function catenceRuntimeHealth() {
9
+ return {
10
+ status: 'ok',
11
+ service: 'catence',
12
+ runtimeVersion: CATENCE_RUNTIME_VERSION,
13
+ protocolVersion: CATENCE_PROTOCOL_VERSION,
14
+ capabilities: { mcp: true, dashboardApi: 1, demoStore: true },
15
+ };
16
+ }
17
+ //# sourceMappingURL=release.js.map
@@ -0,0 +1,11 @@
1
+ /** Paths owned by one isolated local Catence data store. */
2
+ export type CatencePaths = {
3
+ root: string;
4
+ database: string;
5
+ raw: string;
6
+ lake: string;
7
+ staging: string;
8
+ config: string;
9
+ secrets: string;
10
+ lock: string;
11
+ };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=runtime.js.map