cronometer-api-mcp 0.1.0__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.
- cronometer_api_mcp-0.1.0/.github/workflows/ci.yml +24 -0
- cronometer_api_mcp-0.1.0/.github/workflows/publish.yml +25 -0
- cronometer_api_mcp-0.1.0/.gitignore +8 -0
- cronometer_api_mcp-0.1.0/.python-version +1 -0
- cronometer_api_mcp-0.1.0/LICENSE +21 -0
- cronometer_api_mcp-0.1.0/PKG-INFO +225 -0
- cronometer_api_mcp-0.1.0/Procfile +1 -0
- cronometer_api_mcp-0.1.0/README.md +205 -0
- cronometer_api_mcp-0.1.0/pyproject.toml +39 -0
- cronometer_api_mcp-0.1.0/server.json +34 -0
- cronometer_api_mcp-0.1.0/src/cronometer_api_mcp/__init__.py +5 -0
- cronometer_api_mcp-0.1.0/src/cronometer_api_mcp/client.py +699 -0
- cronometer_api_mcp-0.1.0/src/cronometer_api_mcp/server.py +1027 -0
- cronometer_api_mcp-0.1.0/uv.lock +534 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
lint:
|
|
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
|
+
|
|
17
|
+
- name: Install dependencies
|
|
18
|
+
run: uv sync
|
|
19
|
+
|
|
20
|
+
- name: Lint with ruff
|
|
21
|
+
run: uv run ruff check
|
|
22
|
+
|
|
23
|
+
- name: Check formatting with ruff
|
|
24
|
+
run: uv run ruff format --check
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
publish:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
environment: release
|
|
11
|
+
permissions:
|
|
12
|
+
# Required for PyPI trusted publishing via OIDC
|
|
13
|
+
id-token: write
|
|
14
|
+
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- name: Install uv
|
|
19
|
+
uses: astral-sh/setup-uv@v4
|
|
20
|
+
|
|
21
|
+
- name: Build package
|
|
22
|
+
run: uv build
|
|
23
|
+
|
|
24
|
+
- name: Publish to PyPI
|
|
25
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.14
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Randy Westergren
|
|
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,225 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cronometer-api-mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: MCP server for Cronometer nutrition data using the mobile REST API
|
|
5
|
+
Author: Randy
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Keywords: cronometer,health,mcp,model-context-protocol,nutrition
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
|
|
15
|
+
Requires-Python: >=3.14
|
|
16
|
+
Requires-Dist: httpx>=0.27.0
|
|
17
|
+
Requires-Dist: mcp>=1.0.0
|
|
18
|
+
Requires-Dist: uvicorn>=0.30.0
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
# cronometer-api-mcp
|
|
22
|
+
|
|
23
|
+
<!-- mcp-name: io.github.rwestergren/cronometer-api-mcp -->
|
|
24
|
+
|
|
25
|
+
An [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) server for [Cronometer](https://cronometer.com/) nutrition tracking, built on the reverse-engineered mobile REST API.
|
|
26
|
+
|
|
27
|
+
Unlike [cronometer-mcp](https://github.com/cphoskins/cronometer-mcp) which uses the fragile GWT-RPC web protocol (with magic hashes that break on every Cronometer deploy), this server uses the same JSON API that the Cronometer Android app uses -- clean payloads, stable endpoints, no permutation hashes.
|
|
28
|
+
|
|
29
|
+
## Features
|
|
30
|
+
|
|
31
|
+
- **Food log** -- diary entries with food names, amounts, meal groups
|
|
32
|
+
- **Nutrition data** -- daily macro/micro totals and nutrition scores with per-nutrient confidence
|
|
33
|
+
- **Food search** -- search the Cronometer food database, get detailed nutrition info
|
|
34
|
+
- **Diary management** -- add/remove entries, copy days, mark days complete
|
|
35
|
+
- **Custom foods** -- create foods with custom nutrition data
|
|
36
|
+
- **Macro targets** -- read weekly schedule and saved templates
|
|
37
|
+
- **Fasting** -- view history and aggregate statistics
|
|
38
|
+
|
|
39
|
+
## Quick Start
|
|
40
|
+
|
|
41
|
+
### 1. Install [uv](https://docs.astral.sh/uv/)
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### 2. Set credentials
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
export CRONOMETER_USERNAME="your@email.com"
|
|
51
|
+
export CRONOMETER_PASSWORD="your-password"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### 3. Configure your MCP client
|
|
55
|
+
|
|
56
|
+
`uvx` downloads and runs the server on demand -- no separate install step.
|
|
57
|
+
|
|
58
|
+
#### OpenCode (`opencode.json`)
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
{
|
|
62
|
+
"$schema": "https://opencode.ai/config.json",
|
|
63
|
+
"mcp": {
|
|
64
|
+
"cronometer": {
|
|
65
|
+
"type": "local",
|
|
66
|
+
"command": ["uvx", "cronometer-api-mcp"],
|
|
67
|
+
"environment": {
|
|
68
|
+
"CRONOMETER_USERNAME": "{env:CRONOMETER_USERNAME}",
|
|
69
|
+
"CRONOMETER_PASSWORD": "{env:CRONOMETER_PASSWORD}"
|
|
70
|
+
},
|
|
71
|
+
"enabled": true
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
#### Claude Desktop (`claude_desktop_config.json`)
|
|
78
|
+
|
|
79
|
+
```json
|
|
80
|
+
{
|
|
81
|
+
"mcpServers": {
|
|
82
|
+
"cronometer": {
|
|
83
|
+
"command": "uvx",
|
|
84
|
+
"args": ["cronometer-api-mcp"],
|
|
85
|
+
"env": {
|
|
86
|
+
"CRONOMETER_USERNAME": "your@email.com",
|
|
87
|
+
"CRONOMETER_PASSWORD": "your-password"
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Available Tools
|
|
95
|
+
|
|
96
|
+
### Food Log & Nutrition
|
|
97
|
+
|
|
98
|
+
| Tool | Description |
|
|
99
|
+
|------|-------------|
|
|
100
|
+
| `get_food_log` | Diary entries for a date with food names, amounts, and meal groups |
|
|
101
|
+
| `get_daily_nutrition` | Daily macro and micronutrient totals |
|
|
102
|
+
| `get_nutrition_scores` | Category scores (Vitamins, Minerals, etc.) with per-nutrient consumed amounts and confidence levels |
|
|
103
|
+
|
|
104
|
+
### Food Search & Details
|
|
105
|
+
|
|
106
|
+
| Tool | Description |
|
|
107
|
+
|------|-------------|
|
|
108
|
+
| `search_foods` | Search the Cronometer food database by name |
|
|
109
|
+
| `get_food_details` | Full nutrition profile and serving sizes for a food |
|
|
110
|
+
|
|
111
|
+
### Diary Management
|
|
112
|
+
|
|
113
|
+
| Tool | Description |
|
|
114
|
+
|------|-------------|
|
|
115
|
+
| `add_food_entry` | Log a food serving to the diary |
|
|
116
|
+
| `remove_food_entry` | Remove one or more diary entries |
|
|
117
|
+
| `add_custom_food` | Create a custom food with specified nutrition |
|
|
118
|
+
| `copy_day` | Copy all entries from the previous day |
|
|
119
|
+
| `mark_day_complete` | Mark a diary day as complete or incomplete |
|
|
120
|
+
|
|
121
|
+
### Targets & Tracking
|
|
122
|
+
|
|
123
|
+
| Tool | Description |
|
|
124
|
+
|------|-------------|
|
|
125
|
+
| `get_macro_targets` | Weekly macro schedule and saved target templates |
|
|
126
|
+
| `get_fasting_history` | Fasting history within a date range |
|
|
127
|
+
| `get_fasting_stats` | Aggregate fasting statistics |
|
|
128
|
+
|
|
129
|
+
All date parameters use `YYYY-MM-DD` format and default to today when omitted.
|
|
130
|
+
|
|
131
|
+
## Remote Deployment
|
|
132
|
+
|
|
133
|
+
The server supports remote deployment with OAuth 2.1 authorization (PKCE) for use with Claude.ai and other remote MCP clients.
|
|
134
|
+
|
|
135
|
+
### Environment Variables
|
|
136
|
+
|
|
137
|
+
| Variable | Required | Description |
|
|
138
|
+
|----------|----------|-------------|
|
|
139
|
+
| `CRONOMETER_USERNAME` | Yes | Cronometer account email |
|
|
140
|
+
| `CRONOMETER_PASSWORD` | Yes | Cronometer account password |
|
|
141
|
+
| `MCP_TRANSPORT` | No | Transport mode: `stdio` (default), `sse`, or `streamable-http` |
|
|
142
|
+
| `MCP_AUTH_TOKEN` | No | Bearer token for remote auth (enables OAuth flow) |
|
|
143
|
+
| `MCP_OAUTH_CLIENT_ID` | No | OAuth client ID for remote clients |
|
|
144
|
+
| `MCP_OAUTH_CLIENT_SECRET` | No | OAuth client secret for remote clients |
|
|
145
|
+
| `MCP_BASE_URL` | No | Public base URL for OAuth metadata endpoints |
|
|
146
|
+
| `PORT` | No | Listen port for remote transports (default 8000) |
|
|
147
|
+
|
|
148
|
+
### Dokku / Heroku Deployment
|
|
149
|
+
|
|
150
|
+
The project includes a `Procfile` and `.python-version` for direct deployment with the Heroku Python buildpack:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
# Create app
|
|
154
|
+
dokku apps:create cronometer-api-mcp
|
|
155
|
+
|
|
156
|
+
# Set environment
|
|
157
|
+
dokku config:set cronometer-api-mcp \
|
|
158
|
+
MCP_TRANSPORT=streamable-http \
|
|
159
|
+
MCP_AUTH_TOKEN=$(openssl rand -hex 32) \
|
|
160
|
+
MCP_OAUTH_CLIENT_ID=my-client \
|
|
161
|
+
MCP_OAUTH_CLIENT_SECRET=$(openssl rand -hex 32) \
|
|
162
|
+
MCP_BASE_URL=https://your-domain.com \
|
|
163
|
+
CRONOMETER_USERNAME=your@email.com \
|
|
164
|
+
CRONOMETER_PASSWORD=your-password
|
|
165
|
+
|
|
166
|
+
# Deploy
|
|
167
|
+
git push dokku main
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Claude.ai Remote Connection
|
|
171
|
+
|
|
172
|
+
When deployed remotely with OAuth configured, connect from Claude.ai using:
|
|
173
|
+
|
|
174
|
+
- **Server URL**: `https://your-domain.com/mcp`
|
|
175
|
+
- **OAuth Client ID**: Value of `MCP_OAUTH_CLIENT_ID`
|
|
176
|
+
- **OAuth Client Secret**: Value of `MCP_OAUTH_CLIENT_SECRET`
|
|
177
|
+
|
|
178
|
+
Claude.ai will open a browser tab for authorization. Click **Authorize** to complete the connection.
|
|
179
|
+
|
|
180
|
+
## How It Works
|
|
181
|
+
|
|
182
|
+
This server communicates with `mobile.cronometer.com` -- the same REST API used by the Cronometer Android/Flutter app. The API was reverse-engineered through:
|
|
183
|
+
|
|
184
|
+
1. Static analysis of `libapp.so` (Dart AOT snapshot) from the APK to discover endpoint names
|
|
185
|
+
2. Traffic interception via Frida + mitmproxy to capture exact request/response formats
|
|
186
|
+
3. Trial-and-error against the live API to confirm payload shapes
|
|
187
|
+
|
|
188
|
+
The API uses two protocols:
|
|
189
|
+
|
|
190
|
+
- **v2 (`POST /api/v2/*`)** -- JSON-body auth, used for most operations (food search, diary read/write, nutrition, fasting, macros)
|
|
191
|
+
- **v3 (`DELETE /api/v3/user/{id}/*`)** -- Header-based auth (`x-crono-session`), used for diary entry deletion
|
|
192
|
+
|
|
193
|
+
## Python API
|
|
194
|
+
|
|
195
|
+
You can use the client directly:
|
|
196
|
+
|
|
197
|
+
```python
|
|
198
|
+
from cronometer_api_mcp.client import CronometerClient
|
|
199
|
+
from datetime import date
|
|
200
|
+
|
|
201
|
+
client = CronometerClient()
|
|
202
|
+
|
|
203
|
+
# Search for foods
|
|
204
|
+
results = client.search_food("chicken breast")
|
|
205
|
+
|
|
206
|
+
# Get food details
|
|
207
|
+
food = client.get_food(results[0]["id"])
|
|
208
|
+
|
|
209
|
+
# Log a serving
|
|
210
|
+
client.add_serving(
|
|
211
|
+
food_id=food["id"],
|
|
212
|
+
measure_id=food["defaultMeasureId"],
|
|
213
|
+
grams=200,
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
# Get today's diary
|
|
217
|
+
diary = client.get_diary()
|
|
218
|
+
|
|
219
|
+
# Get nutrition scores
|
|
220
|
+
scores = client.get_nutrition_scores()
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## License
|
|
224
|
+
|
|
225
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
web: cronometer-api-mcp
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# cronometer-api-mcp
|
|
2
|
+
|
|
3
|
+
<!-- mcp-name: io.github.rwestergren/cronometer-api-mcp -->
|
|
4
|
+
|
|
5
|
+
An [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) server for [Cronometer](https://cronometer.com/) nutrition tracking, built on the reverse-engineered mobile REST API.
|
|
6
|
+
|
|
7
|
+
Unlike [cronometer-mcp](https://github.com/cphoskins/cronometer-mcp) which uses the fragile GWT-RPC web protocol (with magic hashes that break on every Cronometer deploy), this server uses the same JSON API that the Cronometer Android app uses -- clean payloads, stable endpoints, no permutation hashes.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- **Food log** -- diary entries with food names, amounts, meal groups
|
|
12
|
+
- **Nutrition data** -- daily macro/micro totals and nutrition scores with per-nutrient confidence
|
|
13
|
+
- **Food search** -- search the Cronometer food database, get detailed nutrition info
|
|
14
|
+
- **Diary management** -- add/remove entries, copy days, mark days complete
|
|
15
|
+
- **Custom foods** -- create foods with custom nutrition data
|
|
16
|
+
- **Macro targets** -- read weekly schedule and saved templates
|
|
17
|
+
- **Fasting** -- view history and aggregate statistics
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
### 1. Install [uv](https://docs.astral.sh/uv/)
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### 2. Set credentials
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
export CRONOMETER_USERNAME="your@email.com"
|
|
31
|
+
export CRONOMETER_PASSWORD="your-password"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### 3. Configure your MCP client
|
|
35
|
+
|
|
36
|
+
`uvx` downloads and runs the server on demand -- no separate install step.
|
|
37
|
+
|
|
38
|
+
#### OpenCode (`opencode.json`)
|
|
39
|
+
|
|
40
|
+
```json
|
|
41
|
+
{
|
|
42
|
+
"$schema": "https://opencode.ai/config.json",
|
|
43
|
+
"mcp": {
|
|
44
|
+
"cronometer": {
|
|
45
|
+
"type": "local",
|
|
46
|
+
"command": ["uvx", "cronometer-api-mcp"],
|
|
47
|
+
"environment": {
|
|
48
|
+
"CRONOMETER_USERNAME": "{env:CRONOMETER_USERNAME}",
|
|
49
|
+
"CRONOMETER_PASSWORD": "{env:CRONOMETER_PASSWORD}"
|
|
50
|
+
},
|
|
51
|
+
"enabled": true
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
#### Claude Desktop (`claude_desktop_config.json`)
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"mcpServers": {
|
|
62
|
+
"cronometer": {
|
|
63
|
+
"command": "uvx",
|
|
64
|
+
"args": ["cronometer-api-mcp"],
|
|
65
|
+
"env": {
|
|
66
|
+
"CRONOMETER_USERNAME": "your@email.com",
|
|
67
|
+
"CRONOMETER_PASSWORD": "your-password"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Available Tools
|
|
75
|
+
|
|
76
|
+
### Food Log & Nutrition
|
|
77
|
+
|
|
78
|
+
| Tool | Description |
|
|
79
|
+
|------|-------------|
|
|
80
|
+
| `get_food_log` | Diary entries for a date with food names, amounts, and meal groups |
|
|
81
|
+
| `get_daily_nutrition` | Daily macro and micronutrient totals |
|
|
82
|
+
| `get_nutrition_scores` | Category scores (Vitamins, Minerals, etc.) with per-nutrient consumed amounts and confidence levels |
|
|
83
|
+
|
|
84
|
+
### Food Search & Details
|
|
85
|
+
|
|
86
|
+
| Tool | Description |
|
|
87
|
+
|------|-------------|
|
|
88
|
+
| `search_foods` | Search the Cronometer food database by name |
|
|
89
|
+
| `get_food_details` | Full nutrition profile and serving sizes for a food |
|
|
90
|
+
|
|
91
|
+
### Diary Management
|
|
92
|
+
|
|
93
|
+
| Tool | Description |
|
|
94
|
+
|------|-------------|
|
|
95
|
+
| `add_food_entry` | Log a food serving to the diary |
|
|
96
|
+
| `remove_food_entry` | Remove one or more diary entries |
|
|
97
|
+
| `add_custom_food` | Create a custom food with specified nutrition |
|
|
98
|
+
| `copy_day` | Copy all entries from the previous day |
|
|
99
|
+
| `mark_day_complete` | Mark a diary day as complete or incomplete |
|
|
100
|
+
|
|
101
|
+
### Targets & Tracking
|
|
102
|
+
|
|
103
|
+
| Tool | Description |
|
|
104
|
+
|------|-------------|
|
|
105
|
+
| `get_macro_targets` | Weekly macro schedule and saved target templates |
|
|
106
|
+
| `get_fasting_history` | Fasting history within a date range |
|
|
107
|
+
| `get_fasting_stats` | Aggregate fasting statistics |
|
|
108
|
+
|
|
109
|
+
All date parameters use `YYYY-MM-DD` format and default to today when omitted.
|
|
110
|
+
|
|
111
|
+
## Remote Deployment
|
|
112
|
+
|
|
113
|
+
The server supports remote deployment with OAuth 2.1 authorization (PKCE) for use with Claude.ai and other remote MCP clients.
|
|
114
|
+
|
|
115
|
+
### Environment Variables
|
|
116
|
+
|
|
117
|
+
| Variable | Required | Description |
|
|
118
|
+
|----------|----------|-------------|
|
|
119
|
+
| `CRONOMETER_USERNAME` | Yes | Cronometer account email |
|
|
120
|
+
| `CRONOMETER_PASSWORD` | Yes | Cronometer account password |
|
|
121
|
+
| `MCP_TRANSPORT` | No | Transport mode: `stdio` (default), `sse`, or `streamable-http` |
|
|
122
|
+
| `MCP_AUTH_TOKEN` | No | Bearer token for remote auth (enables OAuth flow) |
|
|
123
|
+
| `MCP_OAUTH_CLIENT_ID` | No | OAuth client ID for remote clients |
|
|
124
|
+
| `MCP_OAUTH_CLIENT_SECRET` | No | OAuth client secret for remote clients |
|
|
125
|
+
| `MCP_BASE_URL` | No | Public base URL for OAuth metadata endpoints |
|
|
126
|
+
| `PORT` | No | Listen port for remote transports (default 8000) |
|
|
127
|
+
|
|
128
|
+
### Dokku / Heroku Deployment
|
|
129
|
+
|
|
130
|
+
The project includes a `Procfile` and `.python-version` for direct deployment with the Heroku Python buildpack:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
# Create app
|
|
134
|
+
dokku apps:create cronometer-api-mcp
|
|
135
|
+
|
|
136
|
+
# Set environment
|
|
137
|
+
dokku config:set cronometer-api-mcp \
|
|
138
|
+
MCP_TRANSPORT=streamable-http \
|
|
139
|
+
MCP_AUTH_TOKEN=$(openssl rand -hex 32) \
|
|
140
|
+
MCP_OAUTH_CLIENT_ID=my-client \
|
|
141
|
+
MCP_OAUTH_CLIENT_SECRET=$(openssl rand -hex 32) \
|
|
142
|
+
MCP_BASE_URL=https://your-domain.com \
|
|
143
|
+
CRONOMETER_USERNAME=your@email.com \
|
|
144
|
+
CRONOMETER_PASSWORD=your-password
|
|
145
|
+
|
|
146
|
+
# Deploy
|
|
147
|
+
git push dokku main
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Claude.ai Remote Connection
|
|
151
|
+
|
|
152
|
+
When deployed remotely with OAuth configured, connect from Claude.ai using:
|
|
153
|
+
|
|
154
|
+
- **Server URL**: `https://your-domain.com/mcp`
|
|
155
|
+
- **OAuth Client ID**: Value of `MCP_OAUTH_CLIENT_ID`
|
|
156
|
+
- **OAuth Client Secret**: Value of `MCP_OAUTH_CLIENT_SECRET`
|
|
157
|
+
|
|
158
|
+
Claude.ai will open a browser tab for authorization. Click **Authorize** to complete the connection.
|
|
159
|
+
|
|
160
|
+
## How It Works
|
|
161
|
+
|
|
162
|
+
This server communicates with `mobile.cronometer.com` -- the same REST API used by the Cronometer Android/Flutter app. The API was reverse-engineered through:
|
|
163
|
+
|
|
164
|
+
1. Static analysis of `libapp.so` (Dart AOT snapshot) from the APK to discover endpoint names
|
|
165
|
+
2. Traffic interception via Frida + mitmproxy to capture exact request/response formats
|
|
166
|
+
3. Trial-and-error against the live API to confirm payload shapes
|
|
167
|
+
|
|
168
|
+
The API uses two protocols:
|
|
169
|
+
|
|
170
|
+
- **v2 (`POST /api/v2/*`)** -- JSON-body auth, used for most operations (food search, diary read/write, nutrition, fasting, macros)
|
|
171
|
+
- **v3 (`DELETE /api/v3/user/{id}/*`)** -- Header-based auth (`x-crono-session`), used for diary entry deletion
|
|
172
|
+
|
|
173
|
+
## Python API
|
|
174
|
+
|
|
175
|
+
You can use the client directly:
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
from cronometer_api_mcp.client import CronometerClient
|
|
179
|
+
from datetime import date
|
|
180
|
+
|
|
181
|
+
client = CronometerClient()
|
|
182
|
+
|
|
183
|
+
# Search for foods
|
|
184
|
+
results = client.search_food("chicken breast")
|
|
185
|
+
|
|
186
|
+
# Get food details
|
|
187
|
+
food = client.get_food(results[0]["id"])
|
|
188
|
+
|
|
189
|
+
# Log a serving
|
|
190
|
+
client.add_serving(
|
|
191
|
+
food_id=food["id"],
|
|
192
|
+
measure_id=food["defaultMeasureId"],
|
|
193
|
+
grams=200,
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
# Get today's diary
|
|
197
|
+
diary = client.get_diary()
|
|
198
|
+
|
|
199
|
+
# Get nutrition scores
|
|
200
|
+
scores = client.get_nutrition_scores()
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## License
|
|
204
|
+
|
|
205
|
+
MIT
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "cronometer-api-mcp"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "MCP server for Cronometer nutrition data using the mobile REST API"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.14"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Randy" },
|
|
14
|
+
]
|
|
15
|
+
keywords = ["cronometer", "mcp", "nutrition", "health", "model-context-protocol"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 3 - Alpha",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.14",
|
|
22
|
+
"Topic :: Scientific/Engineering :: Medical Science Apps.",
|
|
23
|
+
]
|
|
24
|
+
dependencies = [
|
|
25
|
+
"mcp>=1.0.0",
|
|
26
|
+
"httpx>=0.27.0",
|
|
27
|
+
"uvicorn>=0.30.0",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[project.scripts]
|
|
31
|
+
cronometer-api-mcp = "cronometer_api_mcp.server:main"
|
|
32
|
+
|
|
33
|
+
[dependency-groups]
|
|
34
|
+
dev = [
|
|
35
|
+
"ruff>=0.8.0",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
[tool.hatch.build.targets.wheel]
|
|
39
|
+
packages = ["src/cronometer_api_mcp"]
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
|
+
"name": "io.github.rwestergren/cronometer-api-mcp",
|
|
4
|
+
"description": "MCP server for Cronometer nutrition tracking via the mobile REST API — food logs, nutrition scores, diary management, macros, fasting",
|
|
5
|
+
"repository": {
|
|
6
|
+
"url": "https://github.com/rwestergren/cronometer-api-mcp",
|
|
7
|
+
"source": "github"
|
|
8
|
+
},
|
|
9
|
+
"version": "0.1.0",
|
|
10
|
+
"packages": [
|
|
11
|
+
{
|
|
12
|
+
"registryType": "pypi",
|
|
13
|
+
"identifier": "cronometer-api-mcp",
|
|
14
|
+
"version": "0.1.0",
|
|
15
|
+
"transport": {
|
|
16
|
+
"type": "stdio"
|
|
17
|
+
},
|
|
18
|
+
"environmentVariables": [
|
|
19
|
+
{
|
|
20
|
+
"name": "CRONOMETER_USERNAME",
|
|
21
|
+
"description": "Your Cronometer account email",
|
|
22
|
+
"isRequired": true,
|
|
23
|
+
"isSecret": false
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"name": "CRONOMETER_PASSWORD",
|
|
27
|
+
"description": "Your Cronometer account password",
|
|
28
|
+
"isRequired": true,
|
|
29
|
+
"isSecret": true
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
}
|