cronometer-api-mcp 0.2.0__tar.gz → 0.2.2__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.
Files changed (22) hide show
  1. cronometer_api_mcp-0.2.2/.github/dependabot.yml +13 -0
  2. {cronometer_api_mcp-0.2.0 → cronometer_api_mcp-0.2.2}/Dockerfile +4 -0
  3. {cronometer_api_mcp-0.2.0 → cronometer_api_mcp-0.2.2}/PKG-INFO +14 -4
  4. {cronometer_api_mcp-0.2.0 → cronometer_api_mcp-0.2.2}/README.md +11 -0
  5. {cronometer_api_mcp-0.2.0 → cronometer_api_mcp-0.2.2}/pyproject.toml +9 -3
  6. {cronometer_api_mcp-0.2.0 → cronometer_api_mcp-0.2.2}/server.json +2 -2
  7. {cronometer_api_mcp-0.2.0 → cronometer_api_mcp-0.2.2}/src/cronometer_api_mcp/client.py +533 -62
  8. {cronometer_api_mcp-0.2.0 → cronometer_api_mcp-0.2.2}/src/cronometer_api_mcp/server.py +170 -4
  9. cronometer_api_mcp-0.2.2/tests/test_client.py +1009 -0
  10. cronometer_api_mcp-0.2.2/tests/test_server_import.py +142 -0
  11. {cronometer_api_mcp-0.2.0 → cronometer_api_mcp-0.2.2}/tests/test_timezone.py +1 -1
  12. {cronometer_api_mcp-0.2.0 → cronometer_api_mcp-0.2.2}/uv.lock +101 -52
  13. cronometer_api_mcp-0.2.0/tests/test_client.py +0 -336
  14. {cronometer_api_mcp-0.2.0 → cronometer_api_mcp-0.2.2}/.dockerignore +0 -0
  15. {cronometer_api_mcp-0.2.0 → cronometer_api_mcp-0.2.2}/.env.example +0 -0
  16. {cronometer_api_mcp-0.2.0 → cronometer_api_mcp-0.2.2}/.github/workflows/ci.yml +0 -0
  17. {cronometer_api_mcp-0.2.0 → cronometer_api_mcp-0.2.2}/.github/workflows/docker.yml +0 -0
  18. {cronometer_api_mcp-0.2.0 → cronometer_api_mcp-0.2.2}/.github/workflows/publish.yml +0 -0
  19. {cronometer_api_mcp-0.2.0 → cronometer_api_mcp-0.2.2}/.gitignore +0 -0
  20. {cronometer_api_mcp-0.2.0 → cronometer_api_mcp-0.2.2}/.python-version +0 -0
  21. {cronometer_api_mcp-0.2.0 → cronometer_api_mcp-0.2.2}/LICENSE +0 -0
  22. {cronometer_api_mcp-0.2.0 → cronometer_api_mcp-0.2.2}/src/cronometer_api_mcp/__init__.py +0 -0
@@ -0,0 +1,13 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "uv"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
7
+ # Edit the declared ranges, not just the lock: pyproject.toml is what PyPI
8
+ # consumers resolve against, so a breaking release has to surface there.
9
+ versioning-strategy: increase
10
+ open-pull-requests-limit: 5
11
+ groups:
12
+ dev-tooling:
13
+ dependency-type: "development"
@@ -51,6 +51,10 @@ RUN uv python install 3.14 \
51
51
  # in supergateway's own node_modules to a release that covers the versions the
52
52
  # Python `mcp` package negotiates.
53
53
  #
54
+ # Python mcp 2.x still handshakes at 2025-11-25, which this Node SDK range
55
+ # covers; its 2026-07-28 revision is reachable only via `server/discover`.
56
+ # Re-check this pin only if a release adds a *handshake* version.
57
+ #
54
58
  # NOTE: this is a per-image mitigation, not the durable fix. supergateway
55
59
  # proxies `initialize` verbatim but then validates the negotiated version
56
60
  # against its own (possibly older) SDK, so the skew recurs whenever the wrapped
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: cronometer-api-mcp
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: MCP server for Cronometer nutrition data using the mobile REST API
5
5
  Author: Randy
6
6
  License-Expression: MIT
@@ -11,10 +11,9 @@ Classifier: Intended Audience :: Developers
11
11
  Classifier: License :: OSI Approved :: MIT License
12
12
  Classifier: Programming Language :: Python :: 3
13
13
  Classifier: Programming Language :: Python :: 3.14
14
- Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
15
14
  Requires-Python: >=3.14
16
15
  Requires-Dist: httpx[socks]>=0.27.0
17
- Requires-Dist: mcp>=1.0.0
16
+ Requires-Dist: mcp<3,>=2.0.0
18
17
  Requires-Dist: python-dotenv>=1.0.0
19
18
  Requires-Dist: tzdata>=2024.1
20
19
  Description-Content-Type: text/markdown
@@ -138,6 +137,8 @@ cached session, so it also overrides a stale cached timezone.
138
137
  | `add_food_entry` | Log a food serving to the diary |
139
138
  | `remove_food_entry` | Remove one or more diary entries |
140
139
  | `add_custom_food` | Create a custom food with specified nutrition |
140
+ | `add_recipe` | Create a recipe from existing foods referenced by ID and gram weight |
141
+ | `import_recipe` | Create a recipe from a free-text ingredient list; Cronometer matches each line to a database food and converts the amount to grams |
141
142
  | `copy_day` | Copy all entries from the previous day |
142
143
  | `mark_day_complete` | Mark a diary day as complete or incomplete |
143
144
 
@@ -186,6 +187,8 @@ The API uses two protocols:
186
187
  - **v2 (`POST /api/v2/*`)** -- JSON-body auth, used for most operations (food search, diary read/write, nutrition, fasting, macros, biometrics)
187
188
  - **v3 (`DELETE /api/v3/user/{id}/*`)** -- Header-based auth (`x-crono-session`), used for diary entry deletion
188
189
 
190
+ Recipe import is the one asynchronous operation: `import_recipe` returns a job id, and `poll_async_result` is polled until the server reports 100% progress and attaches the parsed ingredients.
191
+
189
192
  ## Python API
190
193
 
191
194
  You can use the client directly:
@@ -212,6 +215,13 @@ client.add_serving(
212
215
  # Get today's diary
213
216
  diary = client.get_diary()
214
217
 
218
+ # Import a recipe from a free-text ingredient list
219
+ recipe = client.import_recipe("one hot dog\nketchup\nbun")
220
+ print(recipe["food_id"], recipe["ingredients"])
221
+
222
+ # Parse without saving, to review the matches first
223
+ preview = client.import_recipe("2 tbsp olive oil\n200g chicken", save=False)
224
+
215
225
  # Get nutrition scores
216
226
  scores = client.get_nutrition_scores()
217
227
  ```
@@ -117,6 +117,8 @@ cached session, so it also overrides a stale cached timezone.
117
117
  | `add_food_entry` | Log a food serving to the diary |
118
118
  | `remove_food_entry` | Remove one or more diary entries |
119
119
  | `add_custom_food` | Create a custom food with specified nutrition |
120
+ | `add_recipe` | Create a recipe from existing foods referenced by ID and gram weight |
121
+ | `import_recipe` | Create a recipe from a free-text ingredient list; Cronometer matches each line to a database food and converts the amount to grams |
120
122
  | `copy_day` | Copy all entries from the previous day |
121
123
  | `mark_day_complete` | Mark a diary day as complete or incomplete |
122
124
 
@@ -165,6 +167,8 @@ The API uses two protocols:
165
167
  - **v2 (`POST /api/v2/*`)** -- JSON-body auth, used for most operations (food search, diary read/write, nutrition, fasting, macros, biometrics)
166
168
  - **v3 (`DELETE /api/v3/user/{id}/*`)** -- Header-based auth (`x-crono-session`), used for diary entry deletion
167
169
 
170
+ Recipe import is the one asynchronous operation: `import_recipe` returns a job id, and `poll_async_result` is polled until the server reports 100% progress and attaches the parsed ingredients.
171
+
168
172
  ## Python API
169
173
 
170
174
  You can use the client directly:
@@ -191,6 +195,13 @@ client.add_serving(
191
195
  # Get today's diary
192
196
  diary = client.get_diary()
193
197
 
198
+ # Import a recipe from a free-text ingredient list
199
+ recipe = client.import_recipe("one hot dog\nketchup\nbun")
200
+ print(recipe["food_id"], recipe["ingredients"])
201
+
202
+ # Parse without saving, to review the matches first
203
+ preview = client.import_recipe("2 tbsp olive oil\n200g chicken", save=False)
204
+
194
205
  # Get nutrition scores
195
206
  scores = client.get_nutrition_scores()
196
207
  ```
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "cronometer-api-mcp"
7
- version = "0.2.0"
7
+ version = "0.2.2"
8
8
  description = "MCP server for Cronometer nutrition data using the mobile REST API"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -19,10 +19,9 @@ classifiers = [
19
19
  "License :: OSI Approved :: MIT License",
20
20
  "Programming Language :: Python :: 3",
21
21
  "Programming Language :: Python :: 3.14",
22
- "Topic :: Scientific/Engineering :: Medical Science Apps.",
23
22
  ]
24
23
  dependencies = [
25
- "mcp>=1.0.0",
24
+ "mcp>=2.0.0,<3",
26
25
  "httpx[socks]>=0.27.0",
27
26
  "python-dotenv>=1.0.0",
28
27
  "tzdata>=2024.1",
@@ -39,3 +38,10 @@ dev = [
39
38
 
40
39
  [tool.hatch.build.targets.wheel]
41
40
  packages = ["src/cronometer_api_mcp"]
41
+
42
+ [tool.ruff.lint.per-file-ignores]
43
+ # Tools have to hand every failure back as a structured error string; an
44
+ # exception escaping into the stdio stream kills the session.
45
+ "src/cronometer_api_mcp/server.py" = ["BLE001"]
46
+ # Diary enrichment is best-effort and degrades to an unenriched diary.
47
+ "src/cronometer_api_mcp/client.py" = ["BLE001"]
@@ -6,12 +6,12 @@
6
6
  "url": "https://github.com/rwestergren/cronometer-api-mcp",
7
7
  "source": "github"
8
8
  },
9
- "version": "0.2.0",
9
+ "version": "0.2.2",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "pypi",
13
13
  "identifier": "cronometer-api-mcp",
14
- "version": "0.2.0",
14
+ "version": "0.2.2",
15
15
  "transport": {
16
16
  "type": "stdio"
17
17
  },