intervals-kit 0.1.1__tar.gz
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.
- intervals_kit-0.1.1/.github/workflows/publish.yml +77 -0
- intervals_kit-0.1.1/.github/workflows/test.yml +28 -0
- intervals_kit-0.1.1/.gitignore +19 -0
- intervals_kit-0.1.1/.python-version +1 -0
- intervals_kit-0.1.1/CLAUDE.md +105 -0
- intervals_kit-0.1.1/LICENSE +21 -0
- intervals_kit-0.1.1/PKG-INFO +281 -0
- intervals_kit-0.1.1/README.md +234 -0
- intervals_kit-0.1.1/SPECIFICATIONS.md +1540 -0
- intervals_kit-0.1.1/openapi-spec.json +1 -0
- intervals_kit-0.1.1/pyproject.toml +57 -0
- intervals_kit-0.1.1/src/intervals_kit/__init__.py +39 -0
- intervals_kit-0.1.1/src/intervals_kit/cli/__init__.py +6 -0
- intervals_kit-0.1.1/src/intervals_kit/cli/commands.py +666 -0
- intervals_kit-0.1.1/src/intervals_kit/cli/main.py +31 -0
- intervals_kit-0.1.1/src/intervals_kit/cli_tools.md +398 -0
- intervals_kit-0.1.1/src/intervals_kit/client.py +126 -0
- intervals_kit-0.1.1/src/intervals_kit/config.py +63 -0
- intervals_kit-0.1.1/src/intervals_kit/errors.py +25 -0
- intervals_kit-0.1.1/src/intervals_kit/exporters.py +43 -0
- intervals_kit-0.1.1/src/intervals_kit/mcp_server.py +778 -0
- intervals_kit-0.1.1/src/intervals_kit/models.py +162 -0
- intervals_kit-0.1.1/src/intervals_kit/service.py +415 -0
- intervals_kit-0.1.1/tests/conftest.py +102 -0
- intervals_kit-0.1.1/tests/test_cli.py +181 -0
- intervals_kit-0.1.1/tests/test_client.py +137 -0
- intervals_kit-0.1.1/tests/test_mcp.py +137 -0
- intervals_kit-0.1.1/tests/test_service.py +230 -0
- intervals_kit-0.1.1/uv.lock +1594 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
|
|
14
|
+
- name: Install uv
|
|
15
|
+
uses: astral-sh/setup-uv@v4
|
|
16
|
+
with:
|
|
17
|
+
python-version: "3.12"
|
|
18
|
+
|
|
19
|
+
- name: Install dependencies
|
|
20
|
+
run: uv sync
|
|
21
|
+
|
|
22
|
+
- name: Run tests
|
|
23
|
+
run: uv run pytest
|
|
24
|
+
|
|
25
|
+
build:
|
|
26
|
+
needs: test
|
|
27
|
+
runs-on: ubuntu-latest
|
|
28
|
+
steps:
|
|
29
|
+
- uses: actions/checkout@v4
|
|
30
|
+
|
|
31
|
+
- name: Install uv
|
|
32
|
+
uses: astral-sh/setup-uv@v4
|
|
33
|
+
with:
|
|
34
|
+
python-version: "3.12"
|
|
35
|
+
|
|
36
|
+
- name: Build package
|
|
37
|
+
run: uv build
|
|
38
|
+
|
|
39
|
+
- name: Upload dist artifacts
|
|
40
|
+
uses: actions/upload-artifact@v4
|
|
41
|
+
with:
|
|
42
|
+
name: dist
|
|
43
|
+
path: dist/
|
|
44
|
+
|
|
45
|
+
publish-testpypi:
|
|
46
|
+
needs: build
|
|
47
|
+
runs-on: ubuntu-latest
|
|
48
|
+
environment: testpypi
|
|
49
|
+
permissions:
|
|
50
|
+
id-token: write
|
|
51
|
+
steps:
|
|
52
|
+
- name: Download dist artifacts
|
|
53
|
+
uses: actions/download-artifact@v4
|
|
54
|
+
with:
|
|
55
|
+
name: dist
|
|
56
|
+
path: dist/
|
|
57
|
+
|
|
58
|
+
- name: Publish to TestPyPI
|
|
59
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
60
|
+
with:
|
|
61
|
+
repository-url: https://test.pypi.org/legacy/
|
|
62
|
+
|
|
63
|
+
publish-pypi:
|
|
64
|
+
needs: publish-testpypi
|
|
65
|
+
runs-on: ubuntu-latest
|
|
66
|
+
environment: pypi
|
|
67
|
+
permissions:
|
|
68
|
+
id-token: write
|
|
69
|
+
steps:
|
|
70
|
+
- name: Download dist artifacts
|
|
71
|
+
uses: actions/download-artifact@v4
|
|
72
|
+
with:
|
|
73
|
+
name: dist
|
|
74
|
+
path: dist/
|
|
75
|
+
|
|
76
|
+
- name: Publish to PyPI
|
|
77
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
name: Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.10", "3.11", "3.12"]
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Install uv
|
|
20
|
+
uses: astral-sh/setup-uv@v4
|
|
21
|
+
with:
|
|
22
|
+
python-version: ${{ matrix.python-version }}
|
|
23
|
+
|
|
24
|
+
- name: Install dependencies
|
|
25
|
+
run: uv sync
|
|
26
|
+
|
|
27
|
+
- name: Run tests
|
|
28
|
+
run: uv run pytest
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## What this project is
|
|
6
|
+
|
|
7
|
+
A Python package providing an MCP server and CLI for the Intervals.ICU REST API. See `SPECIFICATIONS.md` for the architecture spec (used to guide adding new endpoints) and `src/intervals_kit/cli_tools.md` for the full CLI reference.
|
|
8
|
+
|
|
9
|
+
## Commands
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# Setup (first time)
|
|
13
|
+
uv sync
|
|
14
|
+
|
|
15
|
+
# Run MCP server
|
|
16
|
+
uv run intervals-icu-mcp
|
|
17
|
+
|
|
18
|
+
# Run CLI
|
|
19
|
+
uv run intervals-icu-tools --help
|
|
20
|
+
|
|
21
|
+
# Run all tests
|
|
22
|
+
uv run pytest
|
|
23
|
+
|
|
24
|
+
# Run a single test
|
|
25
|
+
uv run pytest tests/test_service.py -k test_name
|
|
26
|
+
|
|
27
|
+
# Run integration tests (real API, requires API key set)
|
|
28
|
+
uv run pytest -m integration
|
|
29
|
+
|
|
30
|
+
# Build for publishing
|
|
31
|
+
uv build
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Build sequence
|
|
35
|
+
|
|
36
|
+
Follow this exact order — each phase depends on the previous one importing cleanly:
|
|
37
|
+
|
|
38
|
+
1. **Phase 1 (Skeleton):** Create dirs, `.python-version`, `pyproject.toml`, run `uv sync`
|
|
39
|
+
2. **Phase 2 (Core):** `errors.py` → `models.py` → `config.py` → `client.py` → `exporters.py` → `service.py`
|
|
40
|
+
- Verify: `uv run python -c "from myapi_tools.service import MyAPIService; print('ok')"`
|
|
41
|
+
3. **Phase 3 (MCP):** `mcp_server.py`
|
|
42
|
+
- Verify: `uv run intervals-icu-tools-mcp &; kill %1`
|
|
43
|
+
4. **Phase 4 (CLI):** `cli/__init__.py` → `cli/main.py` → `cli/commands.py`
|
|
44
|
+
- Verify: `uv run intervals-icu-tools --help`
|
|
45
|
+
5. **Phase 5 (Docs & Tests):** `cli_tools.md`, `tests/`
|
|
46
|
+
6. **Phase 6 (Publish):** `uv build && uv publish`
|
|
47
|
+
|
|
48
|
+
## Architecture
|
|
49
|
+
|
|
50
|
+
Three interfaces, one service layer:
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
MCP Tools (FastMCP) ──┐
|
|
54
|
+
CLI Commands (Click) ──┼──▶ service.py ──▶ client.py ──▶ Intervals.ICU REST API
|
|
55
|
+
Library API (import) ──┘
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
- **`service.py`** — all business logic lives here. MCP tools and CLI commands are thin wrappers.
|
|
59
|
+
- **`client.py`** — low-level `httpx` async client (auth, retry, config).
|
|
60
|
+
- **`models.py`** — Pydantic models for API request/response shapes.
|
|
61
|
+
- **`config.py`** — priority chain: env vars > `~/.config/.../config.toml` > defaults.
|
|
62
|
+
- **`exporters.py`** — output formatting (JSON, CSV, markdown).
|
|
63
|
+
- **`mcp_server.py`** — FastMCP wrapper; registers tools calling service methods.
|
|
64
|
+
- **`cli/`** — Click group + commands; thin wrappers calling service methods.
|
|
65
|
+
- **`__init__.py`** — exports only the core layer (`MyAPIService`, `ApiConfig`, models, errors). Never import `mcp_server` or `cli` here.
|
|
66
|
+
|
|
67
|
+
## Key design rules
|
|
68
|
+
|
|
69
|
+
1. ALL business logic goes in `service.py`. MCP tools and CLI are thin wrappers.
|
|
70
|
+
2. ALL features are exposed via CLI, MCP, AND as importable Python functions.
|
|
71
|
+
3. MCP tools return small structured data (<100 items, <50KB) — fits in LLM context.
|
|
72
|
+
4. CLI commands handle large/binary data (streams to disk, no size cap).
|
|
73
|
+
5. MCP tool docstrings must include: what it does, when to use it, return shape, and the exact `uvx` CLI command for handoff to bulk/binary operations.
|
|
74
|
+
6. CLI prints structured, parseable output — no progress bars, no ANSI color codes.
|
|
75
|
+
7. MCP tools return error dicts on failure (never raise). CLI prints to stderr and exits with structured codes: 0=success, 1=auth/general, 2=rate limit, 3=download, 4=not found.
|
|
76
|
+
|
|
77
|
+
## Deciding what gets an MCP tool vs CLI command
|
|
78
|
+
|
|
79
|
+
| Endpoint type | MCP tool | CLI command |
|
|
80
|
+
|---|---|---|
|
|
81
|
+
| Small structured response (<100 items) | Yes (full data) | Yes |
|
|
82
|
+
| Large dataset (paginated, bulk) | Yes (capped preview, limit=20) | Yes (full download) |
|
|
83
|
+
| Binary/large blob (PDF, ZIP) | Yes (metadata only) | Yes (streaming download) |
|
|
84
|
+
| Async server-side job | Yes (start job, return job ID) | Yes (start + poll + download) |
|
|
85
|
+
| Mutating (POST/PUT/DELETE) | Yes | Yes |
|
|
86
|
+
| Auth/config/health | No — handle in `client.py` | No |
|
|
87
|
+
|
|
88
|
+
## Testing
|
|
89
|
+
|
|
90
|
+
- Mock at the HTTP boundary with `respx`, not at the service layer — tests cover the full stack.
|
|
91
|
+
- Test error paths explicitly (LLM relies on structured error output).
|
|
92
|
+
- Test that all MCP tools have docstrings (missing docstring = silent LLM discoverability failure).
|
|
93
|
+
- Test CLI exit codes (they are part of the contract with calling LLM agents).
|
|
94
|
+
- No real API calls in CI — use `respx` mocks. Real API tests use `@pytest.mark.integration`.
|
|
95
|
+
|
|
96
|
+
## Technology choices
|
|
97
|
+
|
|
98
|
+
| Package | Role | Notes |
|
|
99
|
+
|---|---|---|
|
|
100
|
+
| `fastmcp>=3.0,<4` | MCP server | Import as `from fastmcp import FastMCP`, not the SDK-bundled version |
|
|
101
|
+
| `httpx>=0.27` | HTTP client | Async-native; use `respx` to mock in tests |
|
|
102
|
+
| `pydantic>=2.0` | Data models | Already a transitive dep of FastMCP |
|
|
103
|
+
| `click>=8.0` | CLI | Lighter than `typer` (no `rich`/`shellingham` pull) |
|
|
104
|
+
| `hatchling` | Build backend | Used via `uv build` |
|
|
105
|
+
| `pytest-asyncio` | Async tests | Use `@pytest.mark.asyncio` on async test functions |
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Josua Sassen and Helge Esch
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: intervals-kit
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: MCP server, CLI, and Python library for the Intervals.ICU fitness tracking API
|
|
5
|
+
Project-URL: Homepage, https://github.com/jrsassen/intervals-kit
|
|
6
|
+
Project-URL: Repository, https://github.com/jrsassen/intervals-kit
|
|
7
|
+
Project-URL: Issues, https://github.com/jrsassen/intervals-kit/issues
|
|
8
|
+
Author: Josua Sassen, Helge Esch
|
|
9
|
+
License: MIT License
|
|
10
|
+
|
|
11
|
+
Copyright (c) 2025 Josua Sassen and Helge Esch
|
|
12
|
+
|
|
13
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
14
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
15
|
+
in the Software without restriction, including without limitation the rights
|
|
16
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
17
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
18
|
+
furnished to do so, subject to the following conditions:
|
|
19
|
+
|
|
20
|
+
The above copyright notice and this permission notice shall be included in all
|
|
21
|
+
copies or substantial portions of the Software.
|
|
22
|
+
|
|
23
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
24
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
25
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
26
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
27
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
28
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
29
|
+
SOFTWARE.
|
|
30
|
+
License-File: LICENSE
|
|
31
|
+
Keywords: api,cycling,fitness,intervals,intervals.icu,mcp,triathlon
|
|
32
|
+
Classifier: Development Status :: 4 - Beta
|
|
33
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
34
|
+
Classifier: Programming Language :: Python :: 3
|
|
35
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
36
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
39
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
40
|
+
Requires-Python: >=3.10
|
|
41
|
+
Requires-Dist: click>=8.0
|
|
42
|
+
Requires-Dist: fastmcp<4,>=3.0
|
|
43
|
+
Requires-Dist: httpx>=0.27
|
|
44
|
+
Requires-Dist: pydantic>=2.0
|
|
45
|
+
Requires-Dist: tomli>=2.0; python_version < '3.11'
|
|
46
|
+
Description-Content-Type: text/markdown
|
|
47
|
+
|
|
48
|
+
# intervals-kit
|
|
49
|
+
|
|
50
|
+
MCP server, CLI, and Python library for the [Intervals.ICU](https://intervals.icu) fitness tracking API. Exposes activity data (power curves, HR curves, streams, intervals, segments, file downloads) to AI assistants via the Model Context Protocol, and as a command-line tool for scripting and bulk data access.
|
|
51
|
+
|
|
52
|
+
## Requirements
|
|
53
|
+
|
|
54
|
+
- Python 3.10+
|
|
55
|
+
- [uv](https://docs.astral.sh/uv/getting-started/installation/) — recommended for running the package
|
|
56
|
+
|
|
57
|
+
## Installation
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pip install intervals-kit
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Or with uv:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
uv pip install intervals-kit
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Configuration
|
|
70
|
+
|
|
71
|
+
The package reads credentials from environment variables or a config file.
|
|
72
|
+
|
|
73
|
+
**Environment variables (recommended):**
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
export INTERVALS_API_KEY=your_api_key # From intervals.icu → Settings → API Key
|
|
77
|
+
export INTERVALS_ATHLETE_ID=iXXXXXX # Your athlete ID (visible in the URL when logged in)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Config file** (`~/.config/intervals-kit/config.toml`):
|
|
81
|
+
|
|
82
|
+
```toml
|
|
83
|
+
api_key = "your_api_key"
|
|
84
|
+
athlete_id = "iXXXXXX"
|
|
85
|
+
base_url = "https://intervals.icu" # optional
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Environment variables take precedence over the config file.
|
|
89
|
+
|
|
90
|
+
## Usage as MCP Server (Claude Code / Claude Desktop)
|
|
91
|
+
|
|
92
|
+
The easiest way — no installation needed:
|
|
93
|
+
|
|
94
|
+
```json
|
|
95
|
+
{
|
|
96
|
+
"mcpServers": {
|
|
97
|
+
"intervals-icu": {
|
|
98
|
+
"command": "uvx",
|
|
99
|
+
"args": ["intervals-kit"],
|
|
100
|
+
"env": {
|
|
101
|
+
"INTERVALS_API_KEY": "your_api_key",
|
|
102
|
+
"INTERVALS_ATHLETE_ID": "iXXXXXX"
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Once configured, Claude can directly query your training data:
|
|
110
|
+
|
|
111
|
+
> "How many rides did I do last month?"
|
|
112
|
+
> "What was my best 5-minute power in March?"
|
|
113
|
+
> "Download my activities as a CSV for Q1 2025."
|
|
114
|
+
|
|
115
|
+
### Available MCP Tools
|
|
116
|
+
|
|
117
|
+
| Tool | Description |
|
|
118
|
+
|------|-------------|
|
|
119
|
+
| `list_activities` | List activities for a date range |
|
|
120
|
+
| `get_activity` | Get full details for a single activity |
|
|
121
|
+
| `search_activities` | Search by name or tag |
|
|
122
|
+
| `update_activity` | Update name, description, type, or RPE |
|
|
123
|
+
| `get_activity_intervals` | Get lap/split data |
|
|
124
|
+
| `get_activity_streams` | Get second-by-second time-series (power, HR, cadence, …) |
|
|
125
|
+
| `get_power_curve` | Mean-maximal power curve for an activity |
|
|
126
|
+
| `get_hr_curve` | Heart rate curve for an activity |
|
|
127
|
+
| `get_pace_curve` | Pace curve for running/swimming |
|
|
128
|
+
| `get_best_efforts` | Best efforts detected in an activity |
|
|
129
|
+
| `get_activity_segments` | Matched Strava/Intervals segments |
|
|
130
|
+
| `get_activity_map` | GPS track (lat/lon) |
|
|
131
|
+
| `get_athlete_power_curves` | Best power curves across a date range |
|
|
132
|
+
| `get_athlete_hr_curves` | Best HR curves across a date range |
|
|
133
|
+
|
|
134
|
+
For large data (streams >1 hour, CSV exports, binary files) the MCP tools return metadata and tell Claude to use the CLI for the actual download.
|
|
135
|
+
|
|
136
|
+
## Usage as CLI
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
uvx --from intervals-kit intervals-icu-tools --help
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Activity commands
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
# List activities for a date range (JSON to stdout)
|
|
146
|
+
uvx --from intervals-kit intervals-icu-tools list-activities --oldest 2025-01-01 --newest 2025-03-31
|
|
147
|
+
|
|
148
|
+
# Save to a file instead of stdout
|
|
149
|
+
uvx --from intervals-kit intervals-icu-tools -o ./data list-activities --oldest 2025-01-01 --newest 2025-03-31
|
|
150
|
+
|
|
151
|
+
# Get a single activity
|
|
152
|
+
uvx --from intervals-kit intervals-icu-tools get-activity i136292802
|
|
153
|
+
|
|
154
|
+
# Search by name or tag (# prefix for exact tag match)
|
|
155
|
+
uvx --from intervals-kit intervals-icu-tools search-activities "long ride"
|
|
156
|
+
uvx --from intervals-kit intervals-icu-tools search-activities "#race" --full
|
|
157
|
+
|
|
158
|
+
# Update fields
|
|
159
|
+
uvx --from intervals-kit intervals-icu-tools update-activity i136292802 --perceived-exertion 7 --description "Felt strong"
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Sub-resource commands
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
# Lap/interval data
|
|
166
|
+
uvx --from intervals-kit intervals-icu-tools get-intervals i136292802
|
|
167
|
+
|
|
168
|
+
# Time-series streams (specify types to limit size)
|
|
169
|
+
uvx --from intervals-kit intervals-icu-tools -o ./data get-streams i136292802 --types watts,heartrate,cadence
|
|
170
|
+
|
|
171
|
+
# Power / HR / pace curves
|
|
172
|
+
uvx --from intervals-kit intervals-icu-tools get-power-curve i136292802
|
|
173
|
+
uvx --from intervals-kit intervals-icu-tools get-hr-curve i136292802
|
|
174
|
+
uvx --from intervals-kit intervals-icu-tools get-pace-curve i136292816 # running activity
|
|
175
|
+
|
|
176
|
+
# Best efforts, segments, GPS map
|
|
177
|
+
uvx --from intervals-kit intervals-icu-tools get-best-efforts i136292802
|
|
178
|
+
uvx --from intervals-kit intervals-icu-tools get-segments i136292802
|
|
179
|
+
uvx --from intervals-kit intervals-icu-tools -o ./data get-activity-map i136292802
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### File download commands
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
# Download original upload / FIT / GPX file
|
|
186
|
+
uvx --from intervals-kit intervals-icu-tools -o ./data download-activity-file i136292802 --type fit
|
|
187
|
+
uvx --from intervals-kit intervals-icu-tools -o ./data download-activity-file i136292802 --type gpx
|
|
188
|
+
|
|
189
|
+
# Bulk CSV export for a date range
|
|
190
|
+
uvx --from intervals-kit intervals-icu-tools -o ./data download-activities-csv --oldest 2025-01-01 --newest 2025-03-31
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Aggregate curves
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
# Best power/HR/pace curves across all activities in a date range
|
|
197
|
+
uvx --from intervals-kit intervals-icu-tools get-athlete-power-curves --oldest 2025-01-01 --newest 2025-03-31
|
|
198
|
+
uvx --from intervals-kit intervals-icu-tools get-athlete-hr-curves --oldest 2025-01-01 --newest 2025-03-31
|
|
199
|
+
uvx --from intervals-kit intervals-icu-tools get-athlete-pace-curves --oldest 2025-01-01 --newest 2025-03-31
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Global options
|
|
203
|
+
|
|
204
|
+
| Option | Default | Description |
|
|
205
|
+
|--------|---------|-------------|
|
|
206
|
+
| `-o / --output-dir` | `.` (stdout) | Directory to save output files |
|
|
207
|
+
| `-f / --format` | `json` | Output format: `json` or `csv` |
|
|
208
|
+
|
|
209
|
+
### Exit codes
|
|
210
|
+
|
|
211
|
+
| Code | Meaning |
|
|
212
|
+
|------|---------|
|
|
213
|
+
| 0 | Success |
|
|
214
|
+
| 1 | Auth error or general API error |
|
|
215
|
+
| 2 | Rate limit exceeded (retryable) |
|
|
216
|
+
| 3 | Download / network error (retryable) |
|
|
217
|
+
| 4 | Resource not found |
|
|
218
|
+
|
|
219
|
+
## Usage as Python Library
|
|
220
|
+
|
|
221
|
+
```python
|
|
222
|
+
import asyncio
|
|
223
|
+
from intervals_kit import IntervalsService, load_config
|
|
224
|
+
|
|
225
|
+
async def main():
|
|
226
|
+
svc = IntervalsService(load_config())
|
|
227
|
+
|
|
228
|
+
# List March 2025 activities
|
|
229
|
+
activities = await svc.list_activities("2025-03-01", "2025-03-31")
|
|
230
|
+
rides = [a for a in activities if a.type == "Ride"]
|
|
231
|
+
print(f"Rides: {len(rides)}, Runs: {len(activities) - len(rides)}")
|
|
232
|
+
|
|
233
|
+
# Get power curve for first ride
|
|
234
|
+
if rides:
|
|
235
|
+
curve = await svc.get_power_curve(rides[0].id)
|
|
236
|
+
print(f"Power curve keys: {list(curve.keys())}")
|
|
237
|
+
|
|
238
|
+
asyncio.run(main())
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## Development
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
# Clone and install with dev dependencies
|
|
245
|
+
git clone https://github.com/jrsassen/intervals-kit
|
|
246
|
+
cd intervals-kit
|
|
247
|
+
uv sync
|
|
248
|
+
|
|
249
|
+
# Run tests
|
|
250
|
+
uv run pytest
|
|
251
|
+
|
|
252
|
+
# Run a single test
|
|
253
|
+
uv run pytest tests/test_service.py -k test_name
|
|
254
|
+
|
|
255
|
+
# Run integration tests against the real API (requires credentials)
|
|
256
|
+
uv run pytest -m integration
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Architecture
|
|
260
|
+
|
|
261
|
+
Three interfaces share one service layer — see `SPECIFICATIONS.md` for the full architecture and `src/intervals_kit/cli_tools.md` for the complete CLI reference intended for LLM agents.
|
|
262
|
+
|
|
263
|
+
```
|
|
264
|
+
MCP Tools (FastMCP) ──┐
|
|
265
|
+
CLI Commands (Click) ──┼──▶ service.py ──▶ client.py ──▶ Intervals.ICU API
|
|
266
|
+
Python import ──┘
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Source layout:
|
|
270
|
+
|
|
271
|
+
```
|
|
272
|
+
src/intervals_kit/
|
|
273
|
+
├── errors.py # Exception types
|
|
274
|
+
├── models.py # Pydantic models
|
|
275
|
+
├── config.py # Configuration loading
|
|
276
|
+
├── client.py # HTTP client (auth, error mapping, streaming)
|
|
277
|
+
├── exporters.py # JSON serialization
|
|
278
|
+
├── service.py # All business logic
|
|
279
|
+
├── mcp_server.py # FastMCP tool definitions
|
|
280
|
+
└── cli/ # Click command definitions
|
|
281
|
+
```
|