mcp-grocy 1.11.0 → 2.0.1

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/README.md CHANGED
@@ -63,7 +63,7 @@ Transform your LLM into an intelligent household management assistant with focus
63
63
 
64
64
  # Configure
65
65
  cp .env.example .env
66
- # Edit .env with your GROCY_BASE_URL and GROCY_APIKEY_VALUE
66
+ # Edit .env with your GROCY_BASE_URL and GROCY_API_KEY
67
67
 
68
68
  # Run
69
69
  docker compose up -d
@@ -74,7 +74,7 @@ Test with mock data (no real Grocy instance needed):
74
74
  ```bash
75
75
  # In .env file, any values work for mock mode
76
76
  GROCY_BASE_URL=http://mock
77
- GROCY_APIKEY_VALUE=mock
77
+ GROCY_API_KEY=mock
78
78
 
79
79
  npm install && npm run dev
80
80
  ```
@@ -93,7 +93,7 @@ npm run build
93
93
  ### Docker
94
94
 
95
95
  ```bash
96
- docker run -e GROCY_APIKEY_VALUE=your_api_key -e GROCY_BASE_URL=http://your-grocy-instance ghcr.io/miguelangel-nubla/mcp-grocy:latest
96
+ docker run -e GROCY_API_KEY=your_api_key -e GROCY_BASE_URL=http://your-grocy-instance ghcr.io/miguelangel-nubla/mcp-grocy:latest
97
97
  ```
98
98
 
99
99
  ### Docker Compose (Recommended)
@@ -126,19 +126,19 @@ docker compose up -d
126
126
  2. **Configure the server:**
127
127
  ```bash
128
128
  cp .env.example .env
129
- # Edit .env with your GROCY_BASE_URL and GROCY_APIKEY_VALUE
129
+ # Edit .env with your GROCY_BASE_URL and GROCY_API_KEY
130
130
  ```
131
131
 
132
132
  3. **Essential variables:**
133
133
  - `GROCY_BASE_URL` - Your Grocy instance URL
134
- - `GROCY_APIKEY_VALUE` - Your Grocy API key
134
+ - `GROCY_API_KEY` - Your Grocy API key
135
135
 
136
136
  ### Configuration Options
137
137
 
138
138
  | Method | Use Case | Command |
139
139
  |--------|----------|---------|
140
140
  | **`.env` file** | Recommended for most users | `cp .env.example .env` |
141
- | **Environment variables** | CI/CD, containers | `GROCY_BASE_URL=... GROCY_APIKEY_VALUE=... mcp-grocy` |
141
+ | **Environment variables** | CI/CD, containers | `GROCY_BASE_URL=... GROCY_API_KEY=... mcp-grocy` |
142
142
  | **Tool configuration** | Customize functionality | Edit `tools` section in `mcp-grocy.yaml` |
143
143
 
144
144
  📖 **For complete configuration reference:** See [Configuration Guide](src/resources/config.md)
@@ -185,7 +185,7 @@ npm start
185
185
  - For HTTPS URLs, ensure SSL certificate is valid or disable verification with `GROCY_ENABLE_SSL_VERIFY=false`
186
186
 
187
187
  **"Invalid API key" or "Authentication failed"**
188
- - Verify your `GROCY_APIKEY_VALUE` is correct
188
+ - Verify your `GROCY_API_KEY` is correct
189
189
  - Check that the API key exists in your Grocy instance (User Settings → API Keys)
190
190
  - Ensure the API key has proper permissions
191
191
 
@@ -14,19 +14,17 @@ export class GrocyApiClient {
14
14
  this.setupInterceptors();
15
15
  }
16
16
  createAxiosInstance() {
17
- const { yaml } = config.getConfig();
18
- const apiKey = config.getApiKey();
19
17
  const instance = axios.create({
20
- baseURL: config.getGrocyBaseUrl(),
18
+ baseURL: config.grocy.base_url,
21
19
  validateStatus: () => true, // Handle all status codes manually
22
20
  timeout: 30000,
23
- httpsAgent: yaml.grocy.enable_ssl_verify ? undefined : new https.Agent({
21
+ httpsAgent: config.grocy.enable_ssl_verify ? undefined : new https.Agent({
24
22
  rejectUnauthorized: false
25
23
  })
26
24
  });
27
25
  // Set default authentication
28
- if (apiKey) {
29
- instance.defaults.headers.common[this.API_KEY_HEADER] = apiKey;
26
+ if (config.grocy.api_key) {
27
+ instance.defaults.headers.common[this.API_KEY_HEADER] = config.grocy.api_key;
30
28
  }
31
29
  return instance;
32
30
  }
@@ -11,7 +11,18 @@ import { logger } from '../utils/logger.js';
11
11
  const __dirname = dirname(fileURLToPath(import.meta.url));
12
12
  // Environment schema
13
13
  const EnvironmentSchema = z.object({
14
- GROCY_APIKEY_VALUE: z.string().optional(),
14
+ // Grocy Configuration
15
+ GROCY_BASE_URL: z.string().url().optional(),
16
+ GROCY_API_KEY: z.string().optional(),
17
+ GROCY_ENABLE_SSL_VERIFY: z.enum(['true', 'false']).optional(),
18
+ // Server Configuration
19
+ REST_RESPONSE_SIZE_LIMIT: z.string().regex(/^\d+$/).optional(),
20
+ ENABLE_HTTP_SERVER: z.enum(['true', 'false']).optional(),
21
+ HTTP_SERVER_PORT: z.string().regex(/^\d+$/).optional(),
22
+ // Logging Configuration
23
+ LOG_LEVEL: z.enum(['DEBUG', 'INFO', 'WARN', 'ERROR']).optional(),
24
+ LOG_CATEGORIES: z.string().optional(),
25
+ // Build Configuration
15
26
  RELEASE_VERSION: z.string().optional(),
16
27
  NODE_ENV: z.enum(['development', 'production', 'test']).optional(),
17
28
  });
@@ -23,6 +34,7 @@ const YamlConfigSchema = z.object({
23
34
  }).default({}),
24
35
  grocy: z.object({
25
36
  base_url: z.string().url().default('http://localhost:9283'),
37
+ api_key: z.string().optional(),
26
38
  enable_ssl_verify: z.boolean().default(true),
27
39
  response_size_limit: z.number().positive().default(10000),
28
40
  }).default({}),
@@ -34,8 +46,24 @@ const YamlConfigSchema = z.object({
34
46
  export class ConfigManager {
35
47
  static instance;
36
48
  config;
49
+ // Unified config properties - final resolved values
50
+ grocy;
51
+ server;
52
+ tools;
37
53
  constructor(configPath) {
38
54
  this.config = this.loadConfig(configPath);
55
+ // Expose final resolved values
56
+ this.grocy = {
57
+ base_url: this.config.yaml.grocy.base_url,
58
+ ...(this.config.yaml.grocy.api_key !== undefined && { api_key: this.config.yaml.grocy.api_key }),
59
+ enable_ssl_verify: this.config.yaml.grocy.enable_ssl_verify,
60
+ response_size_limit: this.config.yaml.grocy.response_size_limit
61
+ };
62
+ this.server = {
63
+ enable_http_server: this.config.yaml.server.enable_http_server,
64
+ http_server_port: this.config.yaml.server.http_server_port
65
+ };
66
+ this.tools = this.config.yaml.tools;
39
67
  }
40
68
  static getInstance() {
41
69
  if (!ConfigManager.instance) {
@@ -43,14 +71,13 @@ export class ConfigManager {
43
71
  }
44
72
  return ConfigManager.instance;
45
73
  }
46
- static createForTesting(configPath) {
47
- return new ConfigManager(configPath);
48
- }
49
74
  loadConfig(configPath) {
50
75
  // Load environment variables
51
76
  const env = this.loadEnvironment();
52
77
  // Load YAML configuration
53
78
  const yaml = this.loadYamlConfig(configPath);
79
+ // Apply environment variable overrides
80
+ this.applyEnvironmentOverrides(yaml, env);
54
81
  return { env, yaml };
55
82
  }
56
83
  loadEnvironment() {
@@ -112,26 +139,41 @@ export class ConfigManager {
112
139
  getConfig() {
113
140
  return this.config;
114
141
  }
115
- getGrocyBaseUrl() {
116
- return this.config.yaml.grocy.base_url;
117
- }
118
142
  getApiUrl() {
119
- const baseUrl = this.getGrocyBaseUrl();
120
- return baseUrl.endsWith('/') ? `${baseUrl}api` : `${baseUrl}/api`;
121
- }
122
- hasApiKey() {
123
- return !!this.config.env.GROCY_APIKEY_VALUE;
124
- }
125
- getApiKey() {
126
- return this.config.env.GROCY_APIKEY_VALUE;
143
+ return this.grocy.base_url.endsWith('/') ? `${this.grocy.base_url}api` : `${this.grocy.base_url}/api`;
127
144
  }
128
145
  getCustomHeaders() {
129
146
  const headers = {};
130
- if (this.config.env.GROCY_APIKEY_VALUE) {
131
- headers['GROCY-API-KEY'] = this.config.env.GROCY_APIKEY_VALUE;
147
+ if (this.grocy.api_key) {
148
+ headers['GROCY-API-KEY'] = this.grocy.api_key;
132
149
  }
133
150
  return headers;
134
151
  }
152
+ /**
153
+ * Apply environment variable overrides to YAML configuration
154
+ */
155
+ applyEnvironmentOverrides(yaml, env) {
156
+ // Grocy configuration overrides
157
+ if (env.GROCY_BASE_URL) {
158
+ yaml.grocy.base_url = env.GROCY_BASE_URL;
159
+ }
160
+ if (env.GROCY_API_KEY) {
161
+ yaml.grocy.api_key = env.GROCY_API_KEY;
162
+ }
163
+ if (env.GROCY_ENABLE_SSL_VERIFY !== undefined) {
164
+ yaml.grocy.enable_ssl_verify = env.GROCY_ENABLE_SSL_VERIFY === 'true';
165
+ }
166
+ if (env.REST_RESPONSE_SIZE_LIMIT !== undefined) {
167
+ yaml.grocy.response_size_limit = parseInt(env.REST_RESPONSE_SIZE_LIMIT, 10);
168
+ }
169
+ // Server configuration overrides
170
+ if (env.ENABLE_HTTP_SERVER !== undefined) {
171
+ yaml.server.enable_http_server = env.ENABLE_HTTP_SERVER === 'true';
172
+ }
173
+ if (env.HTTP_SERVER_PORT !== undefined) {
174
+ yaml.server.http_server_port = parseInt(env.HTTP_SERVER_PORT, 10);
175
+ }
176
+ }
135
177
  parseToolConfiguration() {
136
178
  const enabledTools = new Set();
137
179
  const toolSubConfigs = new Map();
package/build/main.js CHANGED
@@ -14,16 +14,14 @@ import { ErrorHandler } from './utils/errors.js';
14
14
  logger.info(`Starting ${SERVER_NAME} v${VERSION}`, 'SERVER');
15
15
  async function main() {
16
16
  return ErrorHandler.handleAsync(async () => {
17
- // Validate configuration
18
- const cfg = config.getConfig();
19
17
  // Check API key
20
- if (!config.hasApiKey()) {
18
+ if (!config.grocy.api_key) {
21
19
  logger.warn('No API key configured. Some operations may fail.', 'CONFIG');
22
20
  }
23
21
  // Log configuration summary
24
- logger.config(`Grocy URL: ${config.getGrocyBaseUrl()}`);
25
- logger.config(`SSL Verify: ${cfg.yaml.grocy.enable_ssl_verify}`);
26
- logger.config(`HTTP Server: ${cfg.yaml.server.enable_http_server}`);
22
+ logger.config(`Grocy URL: ${config.grocy.base_url}`);
23
+ logger.config(`SSL Verify: ${config.grocy.enable_ssl_verify}`);
24
+ logger.config(`HTTP Server: ${config.server.enable_http_server}`);
27
25
  // Create and start server
28
26
  const server = await GrocyMcpServer.create();
29
27
  await server.start();
@@ -63,7 +63,7 @@ Transform your LLM into an intelligent household management assistant with focus
63
63
 
64
64
  # Configure
65
65
  cp .env.example .env
66
- # Edit .env with your GROCY_BASE_URL and GROCY_APIKEY_VALUE
66
+ # Edit .env with your GROCY_BASE_URL and GROCY_API_KEY
67
67
 
68
68
  # Run
69
69
  docker compose up -d
@@ -74,7 +74,7 @@ Test with mock data (no real Grocy instance needed):
74
74
  ```bash
75
75
  # In .env file, any values work for mock mode
76
76
  GROCY_BASE_URL=http://mock
77
- GROCY_APIKEY_VALUE=mock
77
+ GROCY_API_KEY=mock
78
78
 
79
79
  npm install && npm run dev
80
80
  ```
@@ -93,7 +93,7 @@ npm run build
93
93
  ### Docker
94
94
 
95
95
  ```bash
96
- docker run -e GROCY_APIKEY_VALUE=your_api_key -e GROCY_BASE_URL=http://your-grocy-instance ghcr.io/miguelangel-nubla/mcp-grocy:latest
96
+ docker run -e GROCY_API_KEY=your_api_key -e GROCY_BASE_URL=http://your-grocy-instance ghcr.io/miguelangel-nubla/mcp-grocy:latest
97
97
  ```
98
98
 
99
99
  ### Docker Compose (Recommended)
@@ -126,19 +126,19 @@ docker compose up -d
126
126
  2. **Configure the server:**
127
127
  ```bash
128
128
  cp .env.example .env
129
- # Edit .env with your GROCY_BASE_URL and GROCY_APIKEY_VALUE
129
+ # Edit .env with your GROCY_BASE_URL and GROCY_API_KEY
130
130
  ```
131
131
 
132
132
  3. **Essential variables:**
133
133
  - `GROCY_BASE_URL` - Your Grocy instance URL
134
- - `GROCY_APIKEY_VALUE` - Your Grocy API key
134
+ - `GROCY_API_KEY` - Your Grocy API key
135
135
 
136
136
  ### Configuration Options
137
137
 
138
138
  | Method | Use Case | Command |
139
139
  |--------|----------|---------|
140
140
  | **`.env` file** | Recommended for most users | `cp .env.example .env` |
141
- | **Environment variables** | CI/CD, containers | `GROCY_BASE_URL=... GROCY_APIKEY_VALUE=... mcp-grocy` |
141
+ | **Environment variables** | CI/CD, containers | `GROCY_BASE_URL=... GROCY_API_KEY=... mcp-grocy` |
142
142
  | **Tool configuration** | Customize functionality | Edit `tools` section in `mcp-grocy.yaml` |
143
143
 
144
144
  📖 **For complete configuration reference:** See [Configuration Guide](src/resources/config.md)
@@ -185,7 +185,7 @@ npm start
185
185
  - For HTTPS URLs, ensure SSL certificate is valid or disable verification with `GROCY_ENABLE_SSL_VERIFY=false`
186
186
 
187
187
  **"Invalid API key" or "Authentication failed"**
188
- - Verify your `GROCY_APIKEY_VALUE` is correct
188
+ - Verify your `GROCY_API_KEY` is correct
189
189
  - Check that the API key exists in your Grocy instance (User Settings → API Keys)
190
190
  - Ensure the API key has proper permissions
191
191
 
@@ -9,7 +9,7 @@ Advanced configuration reference for the MCP Grocy server. For basic setup, see
9
9
  | Variable | Description | Default | Required |
10
10
  |----------|-------------|---------|----------|
11
11
  | `GROCY_BASE_URL` | Your Grocy instance URL | `http://localhost:9283` | ✅ |
12
- | `GROCY_APIKEY_VALUE` | Your Grocy API key | - | ✅ |
12
+ | `GROCY_API_KEY` | Your Grocy API key | - | ✅ |
13
13
 
14
14
  ### Optional Variables
15
15
 
@@ -77,7 +77,7 @@ Configuration examples for common use cases are provided in `mcp-grocy.yaml.exam
77
77
  ```bash
78
78
  # .env for development
79
79
  GROCY_BASE_URL=http://localhost:9283
80
- GROCY_APIKEY_VALUE=dev_api_key_here
80
+ GROCY_API_KEY=dev_api_key_here
81
81
  GROCY_ENABLE_SSL_VERIFY=false
82
82
  REST_RESPONSE_SIZE_LIMIT=50000
83
83
 
@@ -90,7 +90,7 @@ HTTP_SERVER_PORT=8080
90
90
  ```bash
91
91
  # .env for production
92
92
  GROCY_BASE_URL=https://grocy.yourdomain.com
93
- GROCY_APIKEY_VALUE=secure_production_key
93
+ GROCY_API_KEY=secure_production_key
94
94
  GROCY_ENABLE_SSL_VERIFY=true
95
95
  REST_RESPONSE_SIZE_LIMIT=20000
96
96
  ```
@@ -10,7 +10,7 @@ Before testing the API, you can create your own private Grocy demo instance:
10
10
  4. Use the provided API key and URL in your `.env` file:
11
11
  ```
12
12
  GROCY_BASE_URL=https://your-name-xxxxx.demo.grocy.info
13
- GROCY_APIKEY_VALUE=your-private-api-key
13
+ GROCY_API_KEY=your-private-api-key
14
14
  ```
15
15
 
16
16
  ⚠️ IMPORTANT: Only provide the endpoint path - do not include full URLs. Your path will be automatically resolved to the full URL.
@@ -17,7 +17,7 @@ Edit `claude_desktop_config.json`(for Claude Desktop) or `.cursor/mcp.json`(for
17
17
  ],
18
18
  "env": {
19
19
  "GROCY_BASE_URL": "",
20
- "GROCY_APIKEY_VALUE": "",
20
+ "GROCY_API_KEY": "",
21
21
  "GROCY_ENABLE_SSL_VERIFY": "False",
22
22
  "REST_RESPONSE_SIZE_LIMIT": "10000"
23
23
  }
@@ -41,7 +41,7 @@ Or you can use Docker:
41
41
  ],
42
42
  "env": {
43
43
  "GROCY_BASE_URL": "",
44
- "GROCY_APIKEY_VALUE": "",
44
+ "GROCY_API_KEY": "",
45
45
  "GROCY_ENABLE_SSL_VERIFY": "False",
46
46
  "REST_RESPONSE_SIZE_LIMIT": "10000"
47
47
  }
@@ -67,7 +67,7 @@ Configure your environment with the private demo details:
67
67
 
68
68
  ```
69
69
  GROCY_BASE_URL=https://your-name-xxxxx.demo.grocy.info
70
- GROCY_APIKEY_VALUE=your-private-api-key
70
+ GROCY_API_KEY=your-private-api-key
71
71
  ```
72
72
 
73
73
  These values can be set in your `.env` file for local development or in your project configuration for production use.
@@ -79,7 +79,7 @@ These values can be set in your `.env` file for local development or in your pro
79
79
  - `method`: HTTP method used (e.g., GET, POST, PUT, DELETE).
80
80
  - `headers`: Request headers sent to the Grocy API. Sensitive headers like `GROCY-API-KEY` will have their values redacted.
81
81
  - `body`: Request body sent (if applicable, e.g., for POST/PUT requests).
82
- - `authMethod`: Authentication method used. For Grocy, this will typically be `apikey` if `GROCY_APIKEY_VALUE` is configured, or `none`.
82
+ - `authMethod`: Authentication method used. For Grocy, this will typically be `apikey` if `GROCY_API_KEY` is configured, or `none`.
83
83
 
84
84
  ### Response Details (`response`)
85
85
  - `statusCode`: HTTP status code returned by the Grocy API (e.g., 200, 400, 401).
@@ -113,7 +113,7 @@ export class GrocyMcpServer {
113
113
  }
114
114
  catch (error) {
115
115
  ErrorHandler.logError(error, `tool: ${toolName}`);
116
- throw ErrorHandler.toMcpError(error, `Tool execution failed: ${toolName}`);
116
+ throw ErrorHandler.toMcpError(error, `${toolName} failed`);
117
117
  }
118
118
  });
119
119
  // Resources
@@ -140,12 +140,11 @@ export class GrocyMcpServer {
140
140
  await this.server.connect(transport);
141
141
  logger.info('MCP server running on stdio', 'SERVER');
142
142
  // Start HTTP/SSE if enabled
143
- const { yaml } = config.getConfig();
144
- if (yaml.server.enable_http_server) {
143
+ if (config.server.enable_http_server) {
145
144
  try {
146
- logger.config(`Starting HTTP server on port ${yaml.server.http_server_port}`);
145
+ logger.config(`Starting HTTP server on port ${config.server.http_server_port}`);
147
146
  const serverFactory = () => this.server;
148
- await startHttpServer(serverFactory, yaml.server.http_server_port);
147
+ await startHttpServer(serverFactory, config.server.http_server_port);
149
148
  }
150
149
  catch (error) {
151
150
  logger.error('Failed to start HTTP server', 'SERVER', { error });
@@ -68,17 +68,17 @@ export class BaseToolHandler {
68
68
  /**
69
69
  * Handle tool execution with standardized error handling
70
70
  */
71
- async executeToolHandler(handler, operation) {
71
+ async executeToolHandler(handler) {
72
72
  try {
73
73
  return await handler();
74
74
  }
75
75
  catch (error) {
76
- ErrorHandler.logError(error, operation);
76
+ ErrorHandler.logError(error, 'tool execution');
77
77
  if (error instanceof ValidationError) {
78
78
  return this.createError(error.message);
79
79
  }
80
80
  const message = error instanceof Error ? error.message : 'Internal error';
81
- return this.createError(`${operation} failed: ${message}`);
81
+ return this.createError(`Tool execution failed: ${message}`);
82
82
  }
83
83
  }
84
84
  /**
@@ -95,6 +95,35 @@ export const householdToolDefinitions = [
95
95
  required: ['batteryId']
96
96
  }
97
97
  },
98
+ {
99
+ name: 'household_batteries_print_label',
100
+ description: '[HOUSEHOLD/BATTERIES] Print a Grocycode label for a battery. Use household_batteries_get to find valid batteryId values.',
101
+ inputSchema: {
102
+ type: 'object',
103
+ properties: {
104
+ batteryId: {
105
+ type: 'number',
106
+ description: 'ID of the battery to print label for. Use household_batteries_get tool to find the correct battery ID.'
107
+ }
108
+ },
109
+ required: ['batteryId']
110
+ }
111
+ },
112
+ // ==================== CHORE LABEL PRINTING ====================
113
+ {
114
+ name: 'household_chores_print_label',
115
+ description: '[HOUSEHOLD/CHORES] Print a Grocycode label for a chore. Use household_chores_get to find valid choreId values.',
116
+ inputSchema: {
117
+ type: 'object',
118
+ properties: {
119
+ choreId: {
120
+ type: 'number',
121
+ description: 'ID of the chore to print label for. Use household_chores_get tool to find the correct chore ID.'
122
+ }
123
+ },
124
+ required: ['choreId']
125
+ }
126
+ },
98
127
  // ==================== EQUIPMENT MANAGEMENT ====================
99
128
  {
100
129
  name: 'household_equipment_get',
@@ -5,7 +5,7 @@ export class HouseholdToolHandlers extends BaseToolHandler {
5
5
  return this.executeToolHandler(async () => {
6
6
  const data = await this.apiCall('/objects/chores');
7
7
  return this.createSuccess(data);
8
- }, 'get chores');
8
+ });
9
9
  };
10
10
  trackChoreExecution = async (args) => {
11
11
  return this.executeToolHandler(async () => {
@@ -19,14 +19,14 @@ export class HouseholdToolHandlers extends BaseToolHandler {
19
19
  };
20
20
  const result = await this.apiCall(`/chores/${choreId}/execute`, 'POST', body);
21
21
  return this.createSuccess(result, 'Chore execution tracked successfully');
22
- }, 'track chore execution');
22
+ });
23
23
  };
24
24
  // ==================== TASK MANAGEMENT ====================
25
25
  getTasks = async () => {
26
26
  return this.executeToolHandler(async () => {
27
27
  const data = await this.apiCall('/objects/tasks');
28
28
  return this.createSuccess(data);
29
- }, 'get tasks');
29
+ });
30
30
  };
31
31
  completeTask = async (args) => {
32
32
  return this.executeToolHandler(async () => {
@@ -34,14 +34,14 @@ export class HouseholdToolHandlers extends BaseToolHandler {
34
34
  this.validateRequired({ taskId }, ['taskId']);
35
35
  const result = await this.apiCall(`/tasks/${taskId}/complete`, 'POST', note ? { note } : {});
36
36
  return this.createSuccess(result, 'Task completed successfully');
37
- }, 'complete task');
37
+ });
38
38
  };
39
39
  // ==================== BATTERY MANAGEMENT ====================
40
40
  getBatteries = async () => {
41
41
  return this.executeToolHandler(async () => {
42
42
  const data = await this.apiCall('/objects/batteries');
43
43
  return this.createSuccess(data);
44
- }, 'get batteries');
44
+ });
45
45
  };
46
46
  chargeBattery = async (args) => {
47
47
  return this.executeToolHandler(async () => {
@@ -54,14 +54,31 @@ export class HouseholdToolHandlers extends BaseToolHandler {
54
54
  };
55
55
  const result = await this.apiCall(`/batteries/${batteryId}/charge`, 'POST', body);
56
56
  return this.createSuccess(result, 'Battery charged successfully');
57
- }, 'charge battery');
57
+ });
58
58
  };
59
59
  // ==================== EQUIPMENT MANAGEMENT ====================
60
60
  getEquipment = async () => {
61
61
  return this.executeToolHandler(async () => {
62
62
  const data = await this.apiCall('/objects/equipment');
63
63
  return this.createSuccess(data);
64
- }, 'get equipment');
64
+ });
65
+ };
66
+ // ==================== LABEL PRINTING ====================
67
+ printBatteryLabel = async (args) => {
68
+ return this.executeToolHandler(async () => {
69
+ const { batteryId } = args || {};
70
+ this.validateRequired({ batteryId }, ['batteryId']);
71
+ const result = await this.apiCall(`/batteries/${batteryId}/printlabel`);
72
+ return this.createSuccess(result, 'Battery label printed successfully');
73
+ });
74
+ };
75
+ printChoreLabel = async (args) => {
76
+ return this.executeToolHandler(async () => {
77
+ const { choreId } = args || {};
78
+ this.validateRequired({ choreId }, ['choreId']);
79
+ const result = await this.apiCall(`/chores/${choreId}/printlabel`);
80
+ return this.createSuccess(result, 'Chore label printed successfully');
81
+ });
65
82
  };
66
83
  // ==================== ACTION UTILITIES ====================
67
84
  undoAction = async (args) => {
@@ -87,6 +104,6 @@ export class HouseholdToolHandlers extends BaseToolHandler {
87
104
  }
88
105
  const result = await this.apiCall(endpoint, 'POST');
89
106
  return this.createSuccess(result, `${entityType} action undone successfully`);
90
- }, 'undo action');
107
+ });
91
108
  };
92
109
  }
@@ -13,6 +13,9 @@ export const householdModule = {
13
13
  // Battery Management
14
14
  household_batteries_get: handlers.getBatteries,
15
15
  household_batteries_charge: handlers.chargeBattery,
16
+ household_batteries_print_label: handlers.printBatteryLabel,
17
+ // Chore Label Printing
18
+ household_chores_print_label: handlers.printChoreLabel,
16
19
  // Equipment Management
17
20
  household_equipment_get: handlers.getEquipment,
18
21
  // Action Utilities
@@ -1,5 +1,2 @@
1
1
  // Re-export simplified module loader functionality
2
2
  export { createToolRegistry } from './module-loader.js';
3
- // Export types and modules for testing
4
- export * from './types.js';
5
- export * from './base.js';
@@ -257,7 +257,21 @@ export const inventoryToolDefinitions = [
257
257
  }
258
258
  },
259
259
  {
260
- name: 'inventory_stock_print_label',
260
+ name: 'inventory_products_print_label',
261
+ description: '[INVENTORY/PRODUCTS] Print a Grocycode label for a product. Use inventory_products_get to find valid productId values.',
262
+ inputSchema: {
263
+ type: 'object',
264
+ properties: {
265
+ productId: {
266
+ type: 'number',
267
+ description: 'ID of the product to print label for. Use inventory_products_get tool to find the correct product ID.'
268
+ }
269
+ },
270
+ required: ['productId']
271
+ }
272
+ },
273
+ {
274
+ name: 'inventory_stock_entry_print_label',
261
275
  description: '[INVENTORY/STOCK] Print a label for a specific stock entry. Use inventory_stock_get_by_product to find valid stockId values.',
262
276
  inputSchema: {
263
277
  type: 'object',
@@ -273,5 +287,93 @@ export const inventoryToolDefinitions = [
273
287
  },
274
288
  required: ['stockId', 'productId']
275
289
  }
290
+ },
291
+ // ==================== GRANULAR STOCK ENTRY OPERATIONS ====================
292
+ {
293
+ name: 'inventory_stock_entry_consume',
294
+ description: '[INVENTORY/STOCK] Consume from a specific stock entry. Use inventory_stock_get_by_product to find specific stockId values.',
295
+ inputSchema: {
296
+ type: 'object',
297
+ properties: {
298
+ stockId: {
299
+ type: 'number',
300
+ description: 'ID of the specific stock entry to consume from.'
301
+ },
302
+ productId: {
303
+ type: 'number',
304
+ description: 'ID of the product being consumed. This is required for verification - if you know the stockId, you must know the productId.'
305
+ },
306
+ amount: {
307
+ type: 'number',
308
+ description: 'Amount to consume in the product\'s stock unit (e.g., 1 piece, 0.5 kg, 250 ml). Ensure you know the product\'s stock unit before specifying amount.'
309
+ },
310
+ spoiled: {
311
+ type: 'boolean',
312
+ description: 'Whether the product is spoiled (default: false)',
313
+ default: false
314
+ },
315
+ note: {
316
+ type: 'string',
317
+ description: 'Optional note'
318
+ }
319
+ },
320
+ required: ['stockId', 'productId', 'amount']
321
+ }
322
+ },
323
+ {
324
+ name: 'inventory_stock_entry_transfer',
325
+ description: '[INVENTORY/STOCK] Transfer a specific stock entry to another location. Use inventory_stock_get_by_product to find specific stockId values.',
326
+ inputSchema: {
327
+ type: 'object',
328
+ properties: {
329
+ stockId: {
330
+ type: 'number',
331
+ description: 'ID of the specific stock entry to transfer.'
332
+ },
333
+ productId: {
334
+ type: 'number',
335
+ description: 'ID of the product being transferred. This is required for verification - if you know the stockId, you must know the productId.'
336
+ },
337
+ amount: {
338
+ type: 'number',
339
+ description: 'Amount to transfer in the product\'s stock unit (e.g., 1 piece, 0.5 kg, 250 ml). Ensure you know the product\'s stock unit before specifying amount.'
340
+ },
341
+ locationIdTo: {
342
+ type: 'number',
343
+ description: 'ID of the destination location.'
344
+ },
345
+ note: {
346
+ type: 'string',
347
+ description: 'Optional note for this transfer'
348
+ }
349
+ },
350
+ required: ['stockId', 'productId', 'amount', 'locationIdTo']
351
+ }
352
+ },
353
+ {
354
+ name: 'inventory_stock_entry_open',
355
+ description: '[INVENTORY/STOCK] Mark a specific stock entry as opened. Use inventory_stock_get_by_product to find specific stockId values.',
356
+ inputSchema: {
357
+ type: 'object',
358
+ properties: {
359
+ stockId: {
360
+ type: 'number',
361
+ description: 'ID of the specific stock entry to mark as opened.'
362
+ },
363
+ productId: {
364
+ type: 'number',
365
+ description: 'ID of the product being opened. This is required for verification - if you know the stockId, you must know the productId.'
366
+ },
367
+ amount: {
368
+ type: 'number',
369
+ description: 'Amount to mark as opened in the product\'s stock unit (e.g., 1 piece, 0.5 kg, 200 ml). Ensure you know the product\'s stock unit before specifying amount.'
370
+ },
371
+ note: {
372
+ type: 'string',
373
+ description: 'Optional note'
374
+ }
375
+ },
376
+ required: ['stockId', 'productId', 'amount']
377
+ }
276
378
  }
277
379
  ];