mcp-grocy 2.2.0 → 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.
Files changed (40) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +112 -32
  3. package/build/api/client.js +11 -13
  4. package/build/config/index.js +74 -20
  5. package/build/resources/CHANGELOG.md +31 -0
  6. package/build/resources/DOCS.md +8 -3
  7. package/build/resources/README.md +112 -32
  8. package/build/resources/api-reference.md +88 -88
  9. package/build/resources/config.md +30 -17
  10. package/build/resources/examples.md +120 -221
  11. package/build/resources/installation.md +9 -14
  12. package/build/resources/response-format.md +18 -13
  13. package/build/server/http-server.js +154 -42
  14. package/build/server/mcp-server.js +143 -96
  15. package/build/server/resources.js +37 -24
  16. package/build/server/tool-input-zod.js +86 -0
  17. package/build/tools/base.js +19 -15
  18. package/build/tools/household/definitions.js +47 -43
  19. package/build/tools/household/handlers.js +2 -2
  20. package/build/tools/household/index.js +2 -2
  21. package/build/tools/inventory/definitions.js +150 -113
  22. package/build/tools/inventory/handlers.js +55 -42
  23. package/build/tools/inventory/index.js +2 -2
  24. package/build/tools/module-loader.js +15 -12
  25. package/build/tools/recipes/definitions.js +103 -86
  26. package/build/tools/recipes/handlers.js +44 -38
  27. package/build/tools/recipes/index.js +3 -3
  28. package/build/tools/recipes/validations.js +8 -2
  29. package/build/tools/shopping/definitions.js +22 -20
  30. package/build/tools/shopping/handlers.js +1 -1
  31. package/build/tools/shopping/index.js +2 -2
  32. package/build/tools/system/definitions.js +25 -22
  33. package/build/tools/system/handlers.js +74 -12
  34. package/build/tools/system/index.js +2 -2
  35. package/build/tools/validation-helpers.js +10 -7
  36. package/build/types/index.js +12 -10
  37. package/build/utils/errors.js +10 -5
  38. package/build/utils/logger.js +16 -15
  39. package/build/version.js +2 -2
  40. 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
  [![MCP Compatible](https://img.shields.io/badge/MCP-Compatible-blue)](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 | Use Case | Command |
139
- |--------|----------|---------|
140
- | **`.env` file** | Recommended for most users | `cp .env.example .env` |
141
- | **Environment variables** | CI/CD, containers | `GROCY_BASE_URL=... GROCY_API_KEY=... mcp-grocy` |
142
- | **Tool configuration** | Customize functionality | Edit `tools` section in `mcp-grocy.yaml` |
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 | Purpose | When to Use |
172
- |----------|---------|-------------|
173
- | [📖 API Reference](src/resources/api-reference.md) | Complete tool documentation | Tool usage and examples |
174
- | [⚙️ Configuration Guide](src/resources/config.md) | Advanced configuration reference | Detailed setup, presets, troubleshooting |
175
- | [📋 .env.example](.env.example) | Environment configuration template | Copy and customize for your setup |
176
- | [🧪 MCP Inspector](https://github.com/modelcontextprotocol/inspector) | Protocol debugging | Debug MCP interactions |
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 18 or higher
216
- - Grocy instance (optional with mock mode)
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 | Description |
238
- |---------|-------------|
239
- | `npm run build` | Build TypeScript to JavaScript |
240
- | `npm run watch` | Watch mode for development |
241
- | `npm run dev` | Start with mock data (no Grocy needed) |
242
- | `npm test` | Run test suite |
243
- | `npm run test:watch` | Run tests in watch mode |
244
- | `npm run inspector` | Launch MCP protocol inspector |
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
- *Prioritizing workflow efficiency over feature completeness*
365
+ _Prioritizing workflow efficiency over feature completeness_
@@ -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
- httpsAgent: config.grocy.enable_ssl_verify ? undefined : new https.Agent({
22
- rejectUnauthorized: false
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
- 'Accept': 'application/json',
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
  }
@@ -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
- // Server Configuration
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.object({
31
- server: z.object({
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
- }).default({}),
35
- grocy: z.object({
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
- }).default({}),
41
- tools: z.record(z.string(), z.object({
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
- }).catchall(z.unknown())).default({}),
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 && { api_key: this.config.yaml.grocy.api_key }),
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.errors.forEach(err => {
91
- logger.error(`${err.path.join('.')}: ${err.message}`, 'CONFIG');
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.errors.forEach(err => {
116
- logger.error(`${err.path.join('.')}: ${err.message}`, 'CONFIG');
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('/') ? `${this.grocy.base_url}api` : `${this.grocy.base_url}/api`;
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.