mcp-grocy 2.1.1 → 2.5.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.
- package/CHANGELOG.md +31 -0
- package/README.md +112 -32
- package/build/api/client.js +11 -13
- package/build/config/index.js +74 -20
- package/build/resources/CHANGELOG.md +31 -0
- package/build/resources/DOCS.md +8 -3
- package/build/resources/README.md +112 -32
- package/build/resources/api-reference.md +88 -88
- package/build/resources/config.md +31 -18
- package/build/resources/examples.md +120 -221
- package/build/resources/installation.md +9 -14
- package/build/resources/response-format.md +18 -13
- package/build/server/http-server.js +154 -42
- package/build/server/mcp-server.js +143 -96
- package/build/server/resources.js +37 -24
- package/build/server/tool-input-zod.js +86 -0
- package/build/tools/base.js +19 -15
- package/build/tools/household/definitions.js +47 -43
- package/build/tools/household/handlers.js +2 -2
- package/build/tools/household/index.js +2 -2
- package/build/tools/inventory/definitions.js +150 -113
- package/build/tools/inventory/handlers.js +55 -42
- package/build/tools/inventory/index.js +2 -2
- package/build/tools/module-loader.js +15 -12
- package/build/tools/recipes/definitions.js +103 -86
- package/build/tools/recipes/handlers.js +44 -38
- package/build/tools/recipes/index.js +3 -3
- package/build/tools/recipes/validations.js +8 -2
- package/build/tools/shopping/definitions.js +22 -20
- package/build/tools/shopping/handlers.js +1 -1
- package/build/tools/shopping/index.js +2 -2
- package/build/tools/system/definitions.js +25 -22
- package/build/tools/system/handlers.js +74 -12
- package/build/tools/system/index.js +2 -2
- package/build/tools/validation-helpers.js +10 -7
- package/build/types/index.js +12 -10
- package/build/utils/errors.js +10 -5
- package/build/utils/logger.js +16 -15
- package/build/version.js +2 -2
- package/package.json +38 -23
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# [2.5.0](https://github.com/miguelangel-nubla/mcp-grocy/compare/v2.4.4...v2.5.0) (2026-03-30)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* add read permissions for contents in release workflow to support nested publish-docker job ([5a055a6](https://github.com/miguelangel-nubla/mcp-grocy/commit/5a055a62cf07fa3f0f03d4319e485350c111804a))
|
|
7
|
+
* improve test build isolation and commit-specific tagging ([b1875cc](https://github.com/miguelangel-nubla/mcp-grocy/commit/b1875ccc080111c6d56700aab9c493e418d155be))
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
|
|
12
|
+
* improve tool call error handling, and implement multi-server shutdown management ([4d19cfb](https://github.com/miguelangel-nubla/mcp-grocy/commit/4d19cfb35dc238a41a92d2fba505998b6a59e1ae))
|
|
13
|
+
|
|
14
|
+
# Changelog
|
|
15
|
+
|
|
16
|
+
All notable changes to this project will be documented in this file.
|
|
17
|
+
|
|
18
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
19
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
20
|
+
|
|
21
|
+
Release notes are appended automatically by [semantic-release](https://semantic-release.gitbook.io/) using `@semantic-release/changelog`.
|
|
22
|
+
|
|
23
|
+
## [Unreleased]
|
|
24
|
+
|
|
25
|
+
### ⚠️ Breaking Changes
|
|
26
|
+
|
|
27
|
+
- **MCP resource URIs** use the **`mcp-grocy://`** scheme (from `package.json` `name`, generated into `SERVER_NAME` at build time). Bundled docs: `mcp-grocy://examples`, `mcp-grocy://response-format`, `mcp-grocy://config`. Clients, prompts, or bookmarks that used **`grocy-api://…`** must be updated.
|
|
28
|
+
|
|
29
|
+
### Added
|
|
30
|
+
|
|
31
|
+
- **MCP tool `annotations.readOnlyHint: true`** on read-only tools (inventory/recipes/shopping/household/system getters and lookups only—no `*_print_*`, dev tools, or mutating calls). Optional hint for clients; not a security boundary.
|
package/README.md
CHANGED
|
@@ -8,20 +8,22 @@
|
|
|
8
8
|
[](https://modelcontextprotocol.io)
|
|
9
9
|
|
|
10
10
|
> **🍴 Opinionated Fork Notice**
|
|
11
|
-
>
|
|
11
|
+
>
|
|
12
12
|
> This is a heavily opinionated fork of [saya6k/mcp-grocy-api](https://github.com/saya6k/mcp-grocy-api) that has diverged significantly to warrant a separate identity. This MCP prioritizes **usability over features**.
|
|
13
13
|
>
|
|
14
14
|
> **Why This Fork Exists:**
|
|
15
|
+
>
|
|
15
16
|
> - The original wrapper exposes the entire Grocy API unprocessed, leading to context overload and LLM confusion
|
|
16
17
|
> - Grocy's API design choices and limitations cause error-prone interactions
|
|
17
18
|
> - Generic API exposure increases hallucination and near-miss results
|
|
18
|
-
>
|
|
19
|
+
>
|
|
19
20
|
> **This Fork's Philosophy:**
|
|
21
|
+
>
|
|
20
22
|
> - **Filters and augments data** with relevant context for better LLM comprehension
|
|
21
23
|
> - **Reduces API calls** by combining common operations to minimize error chains
|
|
22
24
|
> - **Optimizes for reliability and repeatability** over feature completeness
|
|
23
25
|
> - **Opinionated workflows** that may not match everyone's preferences
|
|
24
|
-
>
|
|
26
|
+
>
|
|
25
27
|
> If you need complete API access, use the [original fork](https://github.com/saya6k/mcp-grocy-api). This version trades flexibility for focused, dependable grocery management workflows.
|
|
26
28
|
|
|
27
29
|
## 🎯 What This MCP Does
|
|
@@ -29,24 +31,28 @@
|
|
|
29
31
|
Transform your LLM into an intelligent household management assistant with focused tools for:
|
|
30
32
|
|
|
31
33
|
### 📦 **Stock Management**
|
|
34
|
+
|
|
32
35
|
- Track inventory across multiple locations with precision
|
|
33
36
|
- Record purchases and consumption with automatic stock updates
|
|
34
37
|
- Monitor expiry dates and get volatile stock alerts
|
|
35
38
|
- Transfer products between storage locations
|
|
36
39
|
|
|
37
40
|
### 🛒 **Smart Shopping & Planning**
|
|
41
|
+
|
|
38
42
|
- Maintain shopping lists with intelligent quantity management
|
|
39
43
|
- Plan meals with recipe scheduling and fulfillment checking
|
|
40
44
|
- Automatically add missing ingredients to shopping lists
|
|
41
45
|
- Track shopping locations and optimize store visits
|
|
42
46
|
|
|
43
|
-
### 🍽️ **Recipe & Meal Workflows**
|
|
47
|
+
### 🍽️ **Recipe & Meal Workflows**
|
|
48
|
+
|
|
44
49
|
- Find recipes with fuzzy search capabilities
|
|
45
50
|
- Check if recipes can be made with current stock
|
|
46
51
|
- Complete cooking workflows with portion control
|
|
47
52
|
- Integrate meal planning with inventory consumption
|
|
48
53
|
|
|
49
54
|
### 🏠 **Household Management**
|
|
55
|
+
|
|
50
56
|
- Manage chores, tasks, and battery tracking
|
|
51
57
|
- Get product price history for budgeting
|
|
52
58
|
- Organize products by groups and categories
|
|
@@ -56,21 +62,24 @@ Transform your LLM into an intelligent household management assistant with focus
|
|
|
56
62
|
|
|
57
63
|
1. **Get your Grocy API key** from your Grocy instance (User Settings → API Keys)
|
|
58
64
|
2. **Set up with Docker Compose:**
|
|
65
|
+
|
|
59
66
|
```bash
|
|
60
67
|
# Get the project
|
|
61
68
|
git clone https://github.com/miguelangel-nubla/mcp-grocy.git
|
|
62
69
|
cd mcp-grocy
|
|
63
|
-
|
|
70
|
+
|
|
64
71
|
# Configure
|
|
65
72
|
cp .env.example .env
|
|
66
73
|
# Edit .env with your GROCY_BASE_URL and GROCY_API_KEY
|
|
67
|
-
|
|
74
|
+
|
|
68
75
|
# Run
|
|
69
76
|
docker compose up -d
|
|
70
77
|
```
|
|
71
78
|
|
|
72
79
|
### Try Without Grocy
|
|
80
|
+
|
|
73
81
|
Test with mock data (no real Grocy instance needed):
|
|
82
|
+
|
|
74
83
|
```bash
|
|
75
84
|
# In .env file, any values work for mock mode
|
|
76
85
|
GROCY_BASE_URL=http://mock
|
|
@@ -79,6 +88,14 @@ GROCY_API_KEY=mock
|
|
|
79
88
|
npm install && npm run dev
|
|
80
89
|
```
|
|
81
90
|
|
|
91
|
+
## Requirements (Node.js & tooling)
|
|
92
|
+
|
|
93
|
+
- **Node.js:** **22 or newer** as declared in `package.json` `engines`. **GitHub Actions** and **`.nvmrc`** use **Node 22** for CI and local alignment.
|
|
94
|
+
- **Docker:** the default image is **`node:22-alpine`** so the container matches that major version (Home Assistant addon builds still override the base image).
|
|
95
|
+
- **TypeScript:** **5.9** in this repo; **TypeScript 6** is waiting on **`typescript-eslint`** to declare compatible peer support.
|
|
96
|
+
- **`npm audit`:** any remaining findings are often inside **nested tooling** (e.g. bundled `npm`), not application dependencies. Use `npm audit` / `npm audit fix` on a branch when refreshing the lockfile.
|
|
97
|
+
- **Quality checks:** `npm run lint` (ESLint), `npm run format:check` (Prettier), `npm test` (Vitest).
|
|
98
|
+
|
|
82
99
|
## Installation
|
|
83
100
|
|
|
84
101
|
### NPM
|
|
@@ -99,6 +116,7 @@ docker run -e GROCY_API_KEY=your_api_key -e GROCY_BASE_URL=http://your-grocy-ins
|
|
|
99
116
|
### Docker Compose (Recommended)
|
|
100
117
|
|
|
101
118
|
Create a `docker-compose.yml`:
|
|
119
|
+
|
|
102
120
|
```yaml
|
|
103
121
|
services:
|
|
104
122
|
mcp-grocy:
|
|
@@ -109,6 +127,7 @@ services:
|
|
|
109
127
|
```
|
|
110
128
|
|
|
111
129
|
Then:
|
|
130
|
+
|
|
112
131
|
```bash
|
|
113
132
|
cp .env.example .env
|
|
114
133
|
# Edit .env with your configuration
|
|
@@ -124,6 +143,7 @@ docker compose up -d
|
|
|
124
143
|
- Create a new API key and copy it
|
|
125
144
|
|
|
126
145
|
2. **Configure the server:**
|
|
146
|
+
|
|
127
147
|
```bash
|
|
128
148
|
cp .env.example .env
|
|
129
149
|
# Edit .env with your GROCY_BASE_URL and GROCY_API_KEY
|
|
@@ -135,71 +155,83 @@ docker compose up -d
|
|
|
135
155
|
|
|
136
156
|
### Configuration Options
|
|
137
157
|
|
|
138
|
-
| Method
|
|
139
|
-
|
|
140
|
-
| **`.env` file**
|
|
141
|
-
| **Environment variables** | CI/CD, containers
|
|
142
|
-
| **Tool configuration**
|
|
158
|
+
| Method | Use Case | Command |
|
|
159
|
+
| ------------------------- | -------------------------- | ------------------------------------------------ |
|
|
160
|
+
| **`.env` file** | Recommended for most users | `cp .env.example .env` |
|
|
161
|
+
| **Environment variables** | CI/CD, containers | `GROCY_BASE_URL=... GROCY_API_KEY=... mcp-grocy` |
|
|
162
|
+
| **Tool configuration** | Customize functionality | Edit `tools` section in `mcp-grocy.yaml` |
|
|
143
163
|
|
|
144
164
|
📖 **For complete configuration reference:** See [Configuration Guide](src/resources/config.md)
|
|
145
165
|
|
|
146
166
|
## 🚀 Usage Modes
|
|
147
167
|
|
|
148
168
|
### Production Mode
|
|
169
|
+
|
|
149
170
|
Start with your real Grocy instance:
|
|
171
|
+
|
|
150
172
|
```bash
|
|
151
173
|
npm start
|
|
152
174
|
```
|
|
153
175
|
|
|
154
176
|
### Development/Testing Mode
|
|
177
|
+
|
|
155
178
|
Use mock data (no Grocy instance required):
|
|
179
|
+
|
|
156
180
|
```bash
|
|
157
181
|
npm run dev
|
|
158
182
|
```
|
|
159
183
|
|
|
160
|
-
### HTTP Server Mode
|
|
184
|
+
### HTTP Server Mode
|
|
185
|
+
|
|
161
186
|
Enable web-based access via HTTP/SSE:
|
|
187
|
+
|
|
162
188
|
```bash
|
|
163
189
|
# In .env: ENABLE_HTTP_SERVER=true
|
|
164
190
|
npm start
|
|
165
191
|
# Access via http://localhost:8080/mcp
|
|
166
192
|
```
|
|
167
193
|
|
|
168
|
-
|
|
169
194
|
## 📚 Documentation & Resources
|
|
170
195
|
|
|
171
|
-
| Resource
|
|
172
|
-
|
|
173
|
-
| [📖 API Reference](src/resources/api-reference.md)
|
|
174
|
-
| [⚙️ Configuration Guide](src/resources/config.md)
|
|
175
|
-
| [📋 .env.example](.env.example)
|
|
176
|
-
| [🧪 MCP Inspector](https://github.com/modelcontextprotocol/inspector) | Protocol debugging
|
|
196
|
+
| Resource | Purpose | When to Use |
|
|
197
|
+
| --------------------------------------------------------------------- | ---------------------------------- | ---------------------------------------- |
|
|
198
|
+
| [📖 API Reference](src/resources/api-reference.md) | Complete tool documentation | Tool usage and examples |
|
|
199
|
+
| [⚙️ Configuration Guide](src/resources/config.md) | Advanced configuration reference | Detailed setup, presets, troubleshooting |
|
|
200
|
+
| [📋 .env.example](.env.example) | Environment configuration template | Copy and customize for your setup |
|
|
201
|
+
| [🧪 MCP Inspector](https://github.com/modelcontextprotocol/inspector) | Protocol debugging | Debug MCP interactions |
|
|
202
|
+
|
|
203
|
+
**Bundled MCP resources** (from `resources/list`): `mcp-grocy://examples`, `mcp-grocy://response-format`, `mcp-grocy://config` — markdown docs shipped with the server. The URI scheme matches **`package.json` `name`** (previously some builds used `grocy-api://…`; update pinned URIs in clients or prompts if you relied on that).
|
|
177
204
|
|
|
178
205
|
### 🆘 Troubleshooting
|
|
179
206
|
|
|
180
207
|
#### Common Issues
|
|
181
208
|
|
|
182
209
|
**"Connection refused" or "Cannot connect to Grocy"**
|
|
210
|
+
|
|
183
211
|
- Verify `GROCY_BASE_URL` is correct and accessible
|
|
184
212
|
- Check that your Grocy instance is running
|
|
185
213
|
- For HTTPS URLs, ensure SSL certificate is valid or disable verification with `GROCY_ENABLE_SSL_VERIFY=false`
|
|
186
214
|
|
|
187
215
|
**"Invalid API key" or "Authentication failed"**
|
|
216
|
+
|
|
188
217
|
- Verify your `GROCY_API_KEY` is correct
|
|
189
218
|
- Check that the API key exists in your Grocy instance (User Settings → API Keys)
|
|
190
219
|
- Ensure the API key has proper permissions
|
|
191
220
|
|
|
192
221
|
**"Tool not found" errors**
|
|
222
|
+
|
|
193
223
|
- Check if the tool is enabled in your `mcp-grocy.yaml` file
|
|
194
224
|
- Verify you're using the correct tool names from the API reference
|
|
195
225
|
|
|
196
226
|
**Large response errors**
|
|
227
|
+
|
|
197
228
|
- Increase `REST_RESPONSE_SIZE_LIMIT` if you have many products/stock entries
|
|
198
229
|
- Consider disabling unused tools in `mcp-grocy.yaml`
|
|
199
230
|
|
|
200
231
|
#### Debug Mode
|
|
201
232
|
|
|
202
233
|
Enable detailed logging and use the MCP inspector:
|
|
234
|
+
|
|
203
235
|
```bash
|
|
204
236
|
# Launch MCP inspector for protocol debugging
|
|
205
237
|
npm run inspector
|
|
@@ -212,8 +244,8 @@ npm run dev
|
|
|
212
244
|
|
|
213
245
|
### Prerequisites
|
|
214
246
|
|
|
215
|
-
- Node.js
|
|
216
|
-
- Grocy instance (optional
|
|
247
|
+
- Node.js 22 or newer (see **Requirements** above)
|
|
248
|
+
- Grocy instance (optional: use placeholder URLs/keys in `.env` for local runs)
|
|
217
249
|
|
|
218
250
|
### Development Setup
|
|
219
251
|
|
|
@@ -234,18 +266,63 @@ npm start
|
|
|
234
266
|
|
|
235
267
|
### Development Commands
|
|
236
268
|
|
|
237
|
-
| Command
|
|
238
|
-
|
|
239
|
-
| `npm run build`
|
|
240
|
-
| `npm
|
|
241
|
-
| `npm run dev`
|
|
242
|
-
| `npm
|
|
243
|
-
| `npm
|
|
244
|
-
| `npm run
|
|
269
|
+
| Command | Description |
|
|
270
|
+
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- |
|
|
271
|
+
| `npm run build` | Build TypeScript to JavaScript |
|
|
272
|
+
| `npm start` | Run the built server (`build/main.js`) |
|
|
273
|
+
| `npm run dev` | Build, then run (use mock `.env` for local testing) |
|
|
274
|
+
| `npm run watch` | Watch mode for development |
|
|
275
|
+
| `npm test` | Run test suite |
|
|
276
|
+
| `npm run test:watch` | Run tests in watch mode |
|
|
277
|
+
| `npm run inspector` | Launch MCP protocol inspector |
|
|
278
|
+
| `npm run dev:mcp-tef` | Run [mcp-tef](https://github.com/StacklokLabs/mcp-tef) locally (needs **uv** + **Ollama**) for tool-description / similarity checks |
|
|
279
|
+
| `npm run report:mcp-tef` | One command: build, temporary mcp-grocy HTTP + mcp-tef, write `reports/mcp-tef/<timestamp>/` (similarity JSON + `SUMMARY.md`) |
|
|
280
|
+
|
|
281
|
+
### Optional: mcp-tef (local tool evaluation)
|
|
282
|
+
|
|
283
|
+
When you change tool descriptions or add tools, you can run [StacklokLabs/mcp-tef](https://github.com/StacklokLabs/mcp-tef) against **Ollama** (no cloud API key required):
|
|
284
|
+
|
|
285
|
+
1. Install [uv](https://docs.astral.sh/uv/) and start Ollama; pull a small model, e.g. `ollama pull llama3.2:3b`.
|
|
286
|
+
2. Build and start **mcp-grocy over HTTP** in another terminal (SSE endpoint for mcp-tef):
|
|
287
|
+
|
|
288
|
+
```bash
|
|
289
|
+
npm run build
|
|
290
|
+
MCP_HTTP_TRANSPORT_ONLY=true ENABLE_HTTP_SERVER=true HTTP_SERVER_PORT=8790 npm start
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
3. Start mcp-tef:
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
npm run dev:mcp-tef
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
4. Open `http://127.0.0.1:8000/docs` and point workflows at **`http://127.0.0.1:8790/mcp/sse`** (or your port).
|
|
300
|
+
|
|
301
|
+
The first run clones mcp-tef into `.cache/mcp-tef` (ignored by git). Override the Ollama model with `MCP_TEF_OLLAMA_MODEL`, the listen port with `MCP_TEF_PORT`, or the clone ref with `MCP_TEF_REF`.
|
|
302
|
+
|
|
303
|
+
**One-shot report (no manual API calls):**
|
|
304
|
+
|
|
305
|
+
```bash
|
|
306
|
+
npm run report:mcp-tef
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
Writes under `reports/mcp-tef/<timestamp>/`:
|
|
310
|
+
|
|
311
|
+
- **`REPORT.md`** — human-readable: tools by domain, flagged pairs as a **markdown table** (short tool names, similarity %).
|
|
312
|
+
- **`REPORT.html`** — same pairs in a simple table; open in a browser if you prefer.
|
|
313
|
+
- **`SUMMARY.md`** — one-screen pointer + counts.
|
|
314
|
+
- **`similarity.json`** — full API response (matrix, composite ids).
|
|
315
|
+
|
|
316
|
+
Uses ephemeral ports **8792** (mcp-grocy) and **8020** (mcp-tef) by default (`MCP_GROCY_HTTP_PORT`, `MCP_TEF_REPORT_PORT` to override). If you do not set `MCP_GROCY_YAML`, the script drops a temporary `mcp-grocy.yaml` next to the report by copying `mcp-grocy.yaml.example` with every `enabled: false` flipped to `true`, so `tools/list` is complete for analysis.
|
|
317
|
+
|
|
318
|
+
Add `--with-recommendations` for LLM suggestions on flagged pairs, or `--quality` for per-tool quality scoring (slow; both need Ollama). The report calls mcp-tef’s similarity API with **`transport: sse`** against `/mcp/sse` (current [mcp-tef](https://github.com/StacklokLabs/mcp-tef) request shape). Default similarity threshold is **0.9** (set `SIMILARITY_THRESHOLD=0.85` for the previous, noisier report).
|
|
319
|
+
|
|
320
|
+
Optional per-tool **`title`** and **`meta`** (→ MCP `_meta`) can be set in definitions when they add real signal; otherwise clients use **`name`** only.
|
|
245
321
|
|
|
246
322
|
### Debugging
|
|
247
323
|
|
|
248
324
|
Use the MCP inspector to debug protocol interactions:
|
|
325
|
+
|
|
249
326
|
```bash
|
|
250
327
|
npm run inspector
|
|
251
328
|
```
|
|
@@ -257,22 +334,25 @@ This launches a web interface for testing MCP tools and viewing protocol message
|
|
|
257
334
|
This is an **opinionated fork** focused on LLM usability and workflow reliability. Contributions are welcome but must align with the core philosophy:
|
|
258
335
|
|
|
259
336
|
### ✅ Welcome Contributions
|
|
337
|
+
|
|
260
338
|
- Bug fixes and reliability improvements
|
|
261
|
-
- Better error handling and validation
|
|
339
|
+
- Better error handling and validation
|
|
262
340
|
- Documentation improvements
|
|
263
341
|
- Test coverage enhancements
|
|
264
342
|
- Performance optimizations
|
|
265
343
|
|
|
266
344
|
### ❌ Contributions Requiring Discussion
|
|
345
|
+
|
|
267
346
|
- New tool additions (must demonstrate clear LLM workflow benefits)
|
|
268
347
|
- API design changes that increase complexity
|
|
269
348
|
- Features that expose raw Grocy API behavior
|
|
270
349
|
|
|
271
350
|
### Development Workflow
|
|
351
|
+
|
|
272
352
|
1. Fork the repository
|
|
273
353
|
2. Create a feature branch
|
|
274
354
|
3. Make your changes with tests
|
|
275
|
-
4. Run `npm test` and ensure all tests pass
|
|
355
|
+
4. Run `npm test` and ensure all tests pass
|
|
276
356
|
5. Submit a pull request with clear description
|
|
277
357
|
|
|
278
358
|
## 📄 License
|
|
@@ -282,4 +362,4 @@ This project is licensed under the [MIT License](LICENSE).
|
|
|
282
362
|
---
|
|
283
363
|
|
|
284
364
|
**🏠 Made for reliable household management with LLMs**
|
|
285
|
-
|
|
365
|
+
_Prioritizing workflow efficiency over feature completeness_
|
package/build/api/client.js
CHANGED
|
@@ -8,7 +8,6 @@ import { logger } from '../utils/logger.js';
|
|
|
8
8
|
import { ApiError, ErrorHandler } from '../utils/errors.js';
|
|
9
9
|
export class GrocyApiClient {
|
|
10
10
|
axiosInstance;
|
|
11
|
-
API_KEY_HEADER = 'GROCY-API-KEY';
|
|
12
11
|
constructor() {
|
|
13
12
|
this.axiosInstance = this.createAxiosInstance();
|
|
14
13
|
this.setupInterceptors();
|
|
@@ -18,14 +17,13 @@ export class GrocyApiClient {
|
|
|
18
17
|
baseURL: config.grocy.base_url,
|
|
19
18
|
validateStatus: () => true, // Handle all status codes manually
|
|
20
19
|
timeout: 30000,
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
maxContentLength: config.grocy.max_response_bytes,
|
|
21
|
+
httpsAgent: config.grocy.enable_ssl_verify
|
|
22
|
+
? undefined
|
|
23
|
+
: new https.Agent({
|
|
24
|
+
rejectUnauthorized: false,
|
|
25
|
+
}),
|
|
24
26
|
});
|
|
25
|
-
// Set default authentication
|
|
26
|
-
if (config.grocy.api_key) {
|
|
27
|
-
instance.defaults.headers.common[this.API_KEY_HEADER] = config.grocy.api_key;
|
|
28
|
-
}
|
|
29
27
|
return instance;
|
|
30
28
|
}
|
|
31
29
|
setupInterceptors() {
|
|
@@ -42,7 +40,7 @@ export class GrocyApiClient {
|
|
|
42
40
|
if (response.status >= 400) {
|
|
43
41
|
logger.warn(`HTTP ${response.status}`, 'API', {
|
|
44
42
|
url: response.config?.url,
|
|
45
|
-
status: response.status
|
|
43
|
+
status: response.status,
|
|
46
44
|
});
|
|
47
45
|
}
|
|
48
46
|
return response;
|
|
@@ -76,12 +74,12 @@ export class GrocyApiClient {
|
|
|
76
74
|
method,
|
|
77
75
|
url,
|
|
78
76
|
headers: {
|
|
79
|
-
|
|
77
|
+
Accept: 'application/json',
|
|
80
78
|
'Content-Type': 'application/json',
|
|
81
79
|
...config.getCustomHeaders(),
|
|
82
|
-
...headers
|
|
80
|
+
...headers,
|
|
83
81
|
},
|
|
84
|
-
...(timeout && { timeout })
|
|
82
|
+
...(timeout && { timeout }),
|
|
85
83
|
};
|
|
86
84
|
if (['POST', 'PUT', 'PATCH'].includes(method) && body !== null) {
|
|
87
85
|
requestConfig.data = body;
|
|
@@ -93,7 +91,7 @@ export class GrocyApiClient {
|
|
|
93
91
|
return {
|
|
94
92
|
data: response.data,
|
|
95
93
|
status: response.status,
|
|
96
|
-
headers: response.headers
|
|
94
|
+
headers: response.headers,
|
|
97
95
|
};
|
|
98
96
|
}, `API ${method} ${endpoint}`);
|
|
99
97
|
}
|
package/build/config/index.js
CHANGED
|
@@ -9,16 +9,21 @@ import { fileURLToPath } from 'url';
|
|
|
9
9
|
import YAML from 'yaml';
|
|
10
10
|
import { logger } from '../utils/logger.js';
|
|
11
11
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
/** Default cap for Grocy HTTP response bodies (axios maxContentLength), in bytes */
|
|
13
|
+
export const DEFAULT_MAX_RESPONSE_BYTES = 52_428_800; // 50 MiB
|
|
12
14
|
// Environment schema
|
|
13
15
|
const EnvironmentSchema = z.object({
|
|
14
16
|
// Grocy Configuration
|
|
15
17
|
GROCY_BASE_URL: z.string().url().optional(),
|
|
16
18
|
GROCY_API_KEY: z.string().optional(),
|
|
17
19
|
GROCY_ENABLE_SSL_VERIFY: z.enum(['true', 'false']).optional(),
|
|
18
|
-
|
|
20
|
+
GROCY_MAX_RESPONSE_BYTES: z.string().regex(/^\d+$/).optional(),
|
|
21
|
+
// Server Configuration
|
|
19
22
|
REST_RESPONSE_SIZE_LIMIT: z.string().regex(/^\d+$/).optional(),
|
|
20
23
|
ENABLE_HTTP_SERVER: z.enum(['true', 'false']).optional(),
|
|
21
24
|
HTTP_SERVER_PORT: z.string().regex(/^\d+$/).optional(),
|
|
25
|
+
HTTP_CORS_ORIGIN: z.string().optional(),
|
|
26
|
+
MCP_HTTP_ACCESS_TOKEN: z.string().optional(),
|
|
22
27
|
// Logging Configuration
|
|
23
28
|
LOG_LEVEL: z.enum(['DEBUG', 'INFO', 'WARN', 'ERROR']).optional(),
|
|
24
29
|
LOG_CATEGORIES: z.string().optional(),
|
|
@@ -27,22 +32,49 @@ const EnvironmentSchema = z.object({
|
|
|
27
32
|
NODE_ENV: z.enum(['development', 'production', 'test']).optional(),
|
|
28
33
|
});
|
|
29
34
|
// YAML configuration schema
|
|
30
|
-
const YamlConfigSchema = z
|
|
31
|
-
|
|
35
|
+
const YamlConfigSchema = z
|
|
36
|
+
.object({
|
|
37
|
+
server: z
|
|
38
|
+
.object({
|
|
32
39
|
enable_http_server: z.boolean().default(false),
|
|
33
40
|
http_server_port: z.number().min(1).max(65535).default(8080),
|
|
34
|
-
|
|
35
|
-
|
|
41
|
+
/** CORS `Access-Control-Allow-Origin` for HTTP MCP endpoints (`*` or a single origin URL) */
|
|
42
|
+
http_cors_origin: z.string().min(1).default('*'),
|
|
43
|
+
/** When set, MCP HTTP/SSE routes require `Authorization: Bearer <token>`, `X-MCP-Access-Token`, or `access_token` query (GET only). */
|
|
44
|
+
http_access_token: z.string().optional(),
|
|
45
|
+
})
|
|
46
|
+
.strict()
|
|
47
|
+
.default({
|
|
48
|
+
enable_http_server: false,
|
|
49
|
+
http_server_port: 8080,
|
|
50
|
+
http_cors_origin: '*',
|
|
51
|
+
}),
|
|
52
|
+
grocy: z
|
|
53
|
+
.object({
|
|
36
54
|
base_url: z.string().url().default('http://localhost:9283'),
|
|
37
55
|
api_key: z.string().optional(),
|
|
38
56
|
enable_ssl_verify: z.boolean().default(true),
|
|
39
57
|
response_size_limit: z.number().positive().default(10000),
|
|
40
|
-
|
|
41
|
-
|
|
58
|
+
/** Max Grocy API response body size in bytes (all tools); larger responses fail fast */
|
|
59
|
+
max_response_bytes: z.number().positive().default(DEFAULT_MAX_RESPONSE_BYTES),
|
|
60
|
+
})
|
|
61
|
+
.strict()
|
|
62
|
+
.default({
|
|
63
|
+
base_url: 'http://localhost:9283',
|
|
64
|
+
enable_ssl_verify: true,
|
|
65
|
+
response_size_limit: 10000,
|
|
66
|
+
max_response_bytes: DEFAULT_MAX_RESPONSE_BYTES,
|
|
67
|
+
}),
|
|
68
|
+
tools: z
|
|
69
|
+
.record(z.string(), z
|
|
70
|
+
.object({
|
|
42
71
|
enabled: z.boolean().default(false),
|
|
43
72
|
ack_token: z.string().optional(),
|
|
44
|
-
})
|
|
45
|
-
|
|
73
|
+
})
|
|
74
|
+
.catchall(z.unknown()))
|
|
75
|
+
.default({}),
|
|
76
|
+
})
|
|
77
|
+
.strict();
|
|
46
78
|
export class ConfigManager {
|
|
47
79
|
static instance;
|
|
48
80
|
config;
|
|
@@ -55,13 +87,21 @@ export class ConfigManager {
|
|
|
55
87
|
// Expose final resolved values
|
|
56
88
|
this.grocy = {
|
|
57
89
|
base_url: this.config.yaml.grocy.base_url,
|
|
58
|
-
...(this.config.yaml.grocy.api_key !== undefined && {
|
|
90
|
+
...(this.config.yaml.grocy.api_key !== undefined && {
|
|
91
|
+
api_key: this.config.yaml.grocy.api_key,
|
|
92
|
+
}),
|
|
59
93
|
enable_ssl_verify: this.config.yaml.grocy.enable_ssl_verify,
|
|
60
|
-
response_size_limit: this.config.yaml.grocy.response_size_limit
|
|
94
|
+
response_size_limit: this.config.yaml.grocy.response_size_limit,
|
|
95
|
+
max_response_bytes: this.config.yaml.grocy.max_response_bytes,
|
|
61
96
|
};
|
|
62
97
|
this.server = {
|
|
63
98
|
enable_http_server: this.config.yaml.server.enable_http_server,
|
|
64
|
-
http_server_port: this.config.yaml.server.http_server_port
|
|
99
|
+
http_server_port: this.config.yaml.server.http_server_port,
|
|
100
|
+
http_cors_origin: this.config.yaml.server.http_cors_origin,
|
|
101
|
+
...(this.config.yaml.server.http_access_token !== undefined &&
|
|
102
|
+
this.config.yaml.server.http_access_token !== '' && {
|
|
103
|
+
http_access_token: this.config.yaml.server.http_access_token,
|
|
104
|
+
}),
|
|
65
105
|
};
|
|
66
106
|
this.tools = this.config.yaml.tools;
|
|
67
107
|
}
|
|
@@ -87,9 +127,10 @@ export class ConfigManager {
|
|
|
87
127
|
catch (error) {
|
|
88
128
|
if (error instanceof z.ZodError) {
|
|
89
129
|
logger.error('Invalid environment variables', 'CONFIG');
|
|
90
|
-
error.
|
|
91
|
-
|
|
92
|
-
|
|
130
|
+
for (const issue of error.issues) {
|
|
131
|
+
const path = issue.path?.length ? issue.path.join('.') : '(root)';
|
|
132
|
+
logger.error(`${path}: ${issue.message}`, 'CONFIG');
|
|
133
|
+
}
|
|
93
134
|
process.exit(1);
|
|
94
135
|
}
|
|
95
136
|
throw error;
|
|
@@ -112,9 +153,10 @@ export class ConfigManager {
|
|
|
112
153
|
catch (error) {
|
|
113
154
|
if (error instanceof z.ZodError) {
|
|
114
155
|
logger.error('Invalid YAML configuration', 'CONFIG');
|
|
115
|
-
error.
|
|
116
|
-
|
|
117
|
-
|
|
156
|
+
for (const issue of error.issues) {
|
|
157
|
+
const path = issue.path?.length ? issue.path.join('.') : '(root)';
|
|
158
|
+
logger.error(`${path}: ${issue.message}`, 'CONFIG');
|
|
159
|
+
}
|
|
118
160
|
process.exit(1);
|
|
119
161
|
}
|
|
120
162
|
throw error;
|
|
@@ -133,14 +175,16 @@ export class ConfigManager {
|
|
|
133
175
|
resolve(projectRoot, 'mcp-grocy.yaml'),
|
|
134
176
|
resolve(projectRoot, 'mcp-grocy.yml'),
|
|
135
177
|
];
|
|
136
|
-
return possiblePaths.find(path => existsSync(path)) ?? possiblePaths[0];
|
|
178
|
+
return possiblePaths.find((path) => existsSync(path)) ?? possiblePaths[0];
|
|
137
179
|
}
|
|
138
180
|
// Public getters
|
|
139
181
|
getConfig() {
|
|
140
182
|
return this.config;
|
|
141
183
|
}
|
|
142
184
|
getApiUrl() {
|
|
143
|
-
return this.grocy.base_url.endsWith('/')
|
|
185
|
+
return this.grocy.base_url.endsWith('/')
|
|
186
|
+
? `${this.grocy.base_url}api`
|
|
187
|
+
: `${this.grocy.base_url}/api`;
|
|
144
188
|
}
|
|
145
189
|
getCustomHeaders() {
|
|
146
190
|
const headers = {};
|
|
@@ -166,6 +210,9 @@ export class ConfigManager {
|
|
|
166
210
|
if (env.REST_RESPONSE_SIZE_LIMIT !== undefined) {
|
|
167
211
|
yaml.grocy.response_size_limit = parseInt(env.REST_RESPONSE_SIZE_LIMIT, 10);
|
|
168
212
|
}
|
|
213
|
+
if (env.GROCY_MAX_RESPONSE_BYTES !== undefined) {
|
|
214
|
+
yaml.grocy.max_response_bytes = parseInt(env.GROCY_MAX_RESPONSE_BYTES, 10);
|
|
215
|
+
}
|
|
169
216
|
// Server configuration overrides
|
|
170
217
|
if (env.ENABLE_HTTP_SERVER !== undefined) {
|
|
171
218
|
yaml.server.enable_http_server = env.ENABLE_HTTP_SERVER === 'true';
|
|
@@ -173,6 +220,13 @@ export class ConfigManager {
|
|
|
173
220
|
if (env.HTTP_SERVER_PORT !== undefined) {
|
|
174
221
|
yaml.server.http_server_port = parseInt(env.HTTP_SERVER_PORT, 10);
|
|
175
222
|
}
|
|
223
|
+
if (env.HTTP_CORS_ORIGIN !== undefined && env.HTTP_CORS_ORIGIN.length > 0) {
|
|
224
|
+
yaml.server.http_cors_origin = env.HTTP_CORS_ORIGIN;
|
|
225
|
+
}
|
|
226
|
+
if (env.MCP_HTTP_ACCESS_TOKEN !== undefined) {
|
|
227
|
+
yaml.server.http_access_token =
|
|
228
|
+
env.MCP_HTTP_ACCESS_TOKEN.length > 0 ? env.MCP_HTTP_ACCESS_TOKEN : undefined;
|
|
229
|
+
}
|
|
176
230
|
}
|
|
177
231
|
parseToolConfiguration() {
|
|
178
232
|
const enabledTools = new Set();
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# [2.5.0](https://github.com/miguelangel-nubla/mcp-grocy/compare/v2.4.4...v2.5.0) (2026-03-30)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* add read permissions for contents in release workflow to support nested publish-docker job ([5a055a6](https://github.com/miguelangel-nubla/mcp-grocy/commit/5a055a62cf07fa3f0f03d4319e485350c111804a))
|
|
7
|
+
* improve test build isolation and commit-specific tagging ([b1875cc](https://github.com/miguelangel-nubla/mcp-grocy/commit/b1875ccc080111c6d56700aab9c493e418d155be))
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
|
|
12
|
+
* improve tool call error handling, and implement multi-server shutdown management ([4d19cfb](https://github.com/miguelangel-nubla/mcp-grocy/commit/4d19cfb35dc238a41a92d2fba505998b6a59e1ae))
|
|
13
|
+
|
|
14
|
+
# Changelog
|
|
15
|
+
|
|
16
|
+
All notable changes to this project will be documented in this file.
|
|
17
|
+
|
|
18
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
19
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
20
|
+
|
|
21
|
+
Release notes are appended automatically by [semantic-release](https://semantic-release.gitbook.io/) using `@semantic-release/changelog`.
|
|
22
|
+
|
|
23
|
+
## [Unreleased]
|
|
24
|
+
|
|
25
|
+
### ⚠️ Breaking Changes
|
|
26
|
+
|
|
27
|
+
- **MCP resource URIs** use the **`mcp-grocy://`** scheme (from `package.json` `name`, generated into `SERVER_NAME` at build time). Bundled docs: `mcp-grocy://examples`, `mcp-grocy://response-format`, `mcp-grocy://config`. Clients, prompts, or bookmarks that used **`grocy-api://…`** must be updated.
|
|
28
|
+
|
|
29
|
+
### Added
|
|
30
|
+
|
|
31
|
+
- **MCP tool `annotations.readOnlyHint: true`** on read-only tools (inventory/recipes/shopping/household/system getters and lookups only—no `*_print_*`, dev tools, or mutating calls). Optional hint for clients; not a security boundary.
|