mcp-grocy 1.11.0 → 2.0.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/README.md +7 -7
- package/build/api/client.js +4 -6
- package/build/config/index.js +59 -17
- package/build/main.js +4 -6
- package/build/resources/README.md +7 -7
- package/build/resources/config.md +3 -3
- package/build/resources/examples.md +1 -1
- package/build/resources/installation.md +2 -2
- package/build/resources/response-format.md +2 -2
- package/build/server/mcp-server.js +4 -5
- package/build/tools/base.js +3 -3
- package/build/tools/household/definitions.js +29 -0
- package/build/tools/household/handlers.js +25 -8
- package/build/tools/household/index.js +3 -0
- package/build/tools/index.js +0 -3
- package/build/tools/inventory/definitions.js +103 -1
- package/build/tools/inventory/handlers.js +166 -14
- package/build/tools/inventory/index.js +6 -1
- package/build/tools/module-loader.js +2 -10
- package/build/tools/recipes/definitions.js +17 -3
- package/build/tools/recipes/handlers.js +31 -26
- package/build/tools/recipes/index.js +2 -1
- package/build/tools/recipes/validations.js +3 -3
- package/build/tools/shopping/definitions.js +9 -0
- package/build/tools/shopping/handlers.js +10 -4
- package/build/tools/shopping/index.js +2 -1
- package/build/tools/system/definitions.js +1 -1
- package/build/tools/system/handlers.js +7 -7
- package/build/tools/validation-helpers.js +2 -2
- package/build/types/index.js +1 -1
- package/build/version.js +1 -1
- package/package.json +1 -1
- package/build/config/environment.js +0 -62
- package/build/config/yaml-config.js +0 -203
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
- `
|
|
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=...
|
|
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 `
|
|
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
|
|
package/build/api/client.js
CHANGED
|
@@ -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.
|
|
18
|
+
baseURL: config.grocy.base_url,
|
|
21
19
|
validateStatus: () => true, // Handle all status codes manually
|
|
22
20
|
timeout: 30000,
|
|
23
|
-
httpsAgent:
|
|
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 (
|
|
29
|
-
instance.defaults.headers.common[this.API_KEY_HEADER] =
|
|
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
|
}
|
package/build/config/index.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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.
|
|
131
|
-
headers['GROCY-API-KEY'] = this.
|
|
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.
|
|
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.
|
|
25
|
-
logger.config(`SSL Verify: ${
|
|
26
|
-
logger.config(`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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
- `
|
|
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=...
|
|
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 `
|
|
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
|
-
| `
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
|
|
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 `
|
|
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,
|
|
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
|
-
|
|
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 ${
|
|
145
|
+
logger.config(`Starting HTTP server on port ${config.server.http_server_port}`);
|
|
147
146
|
const serverFactory = () => this.server;
|
|
148
|
-
await startHttpServer(serverFactory,
|
|
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 });
|
package/build/tools/base.js
CHANGED
|
@@ -68,17 +68,17 @@ export class BaseToolHandler {
|
|
|
68
68
|
/**
|
|
69
69
|
* Handle tool execution with standardized error handling
|
|
70
70
|
*/
|
|
71
|
-
async executeToolHandler(handler
|
|
71
|
+
async executeToolHandler(handler) {
|
|
72
72
|
try {
|
|
73
73
|
return await handler();
|
|
74
74
|
}
|
|
75
75
|
catch (error) {
|
|
76
|
-
ErrorHandler.logError(error,
|
|
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(
|
|
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
|
-
}
|
|
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
|
-
}
|
|
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
|
-
}
|
|
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
|
-
}
|
|
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
|
-
}
|
|
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
|
-
}
|
|
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
|
-
}
|
|
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
|
-
}
|
|
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
|
package/build/tools/index.js
CHANGED
|
@@ -257,7 +257,21 @@ export const inventoryToolDefinitions = [
|
|
|
257
257
|
}
|
|
258
258
|
},
|
|
259
259
|
{
|
|
260
|
-
name: '
|
|
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
|
];
|